roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957 /*
958  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(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 = 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  * Based on:
4902  * Ext JS Library 1.1.1
4903  * Copyright(c) 2006-2007, Ext JS, LLC.
4904  *
4905  * Originally Released Under LGPL - original licence link has changed is not relivant.
4906  *
4907  * Fork - LGPL
4908  * <script type="text/javascript">
4909  */
4910
4911
4912 // nasty IE9 hack - what a pile of crap that is..
4913
4914  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4915     Range.prototype.createContextualFragment = function (html) {
4916         var doc = window.document;
4917         var container = doc.createElement("div");
4918         container.innerHTML = html;
4919         var frag = doc.createDocumentFragment(), n;
4920         while ((n = container.firstChild)) {
4921             frag.appendChild(n);
4922         }
4923         return frag;
4924     };
4925 }
4926
4927 /**
4928  * @class Roo.DomHelper
4929  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4930  * 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>.
4931  * @static
4932  */
4933 Roo.DomHelper = function(){
4934     var tempTableEl = null;
4935     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4936     var tableRe = /^table|tbody|tr|td$/i;
4937     var xmlns = {};
4938     // build as innerHTML where available
4939     /** @ignore */
4940     var createHtml = function(o){
4941         if(typeof o == 'string'){
4942             return o;
4943         }
4944         var b = "";
4945         if(!o.tag){
4946             o.tag = "div";
4947         }
4948         b += "<" + o.tag;
4949         for(var attr in o){
4950             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4951             if(attr == "style"){
4952                 var s = o["style"];
4953                 if(typeof s == "function"){
4954                     s = s.call();
4955                 }
4956                 if(typeof s == "string"){
4957                     b += ' style="' + s + '"';
4958                 }else if(typeof s == "object"){
4959                     b += ' style="';
4960                     for(var key in s){
4961                         if(typeof s[key] != "function"){
4962                             b += key + ":" + s[key] + ";";
4963                         }
4964                     }
4965                     b += '"';
4966                 }
4967             }else{
4968                 if(attr == "cls"){
4969                     b += ' class="' + o["cls"] + '"';
4970                 }else if(attr == "htmlFor"){
4971                     b += ' for="' + o["htmlFor"] + '"';
4972                 }else{
4973                     b += " " + attr + '="' + o[attr] + '"';
4974                 }
4975             }
4976         }
4977         if(emptyTags.test(o.tag)){
4978             b += "/>";
4979         }else{
4980             b += ">";
4981             var cn = o.children || o.cn;
4982             if(cn){
4983                 //http://bugs.kde.org/show_bug.cgi?id=71506
4984                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4985                     for(var i = 0, len = cn.length; i < len; i++) {
4986                         b += createHtml(cn[i], b);
4987                     }
4988                 }else{
4989                     b += createHtml(cn, b);
4990                 }
4991             }
4992             if(o.html){
4993                 b += o.html;
4994             }
4995             b += "</" + o.tag + ">";
4996         }
4997         return b;
4998     };
4999
5000     // build as dom
5001     /** @ignore */
5002     var createDom = function(o, parentNode){
5003          
5004         // defininition craeted..
5005         var ns = false;
5006         if (o.ns && o.ns != 'html') {
5007                
5008             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5009                 xmlns[o.ns] = o.xmlns;
5010                 ns = o.xmlns;
5011             }
5012             if (typeof(xmlns[o.ns]) == 'undefined') {
5013                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5014             }
5015             ns = xmlns[o.ns];
5016         }
5017         
5018         
5019         if (typeof(o) == 'string') {
5020             return parentNode.appendChild(document.createTextNode(o));
5021         }
5022         o.tag = o.tag || div;
5023         if (o.ns && Roo.isIE) {
5024             ns = false;
5025             o.tag = o.ns + ':' + o.tag;
5026             
5027         }
5028         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5029         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5030         for(var attr in o){
5031             
5032             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5033                     attr == "style" || typeof o[attr] == "function") { continue; }
5034                     
5035             if(attr=="cls" && Roo.isIE){
5036                 el.className = o["cls"];
5037             }else{
5038                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5039                 else { 
5040                     el[attr] = o[attr];
5041                 }
5042             }
5043         }
5044         Roo.DomHelper.applyStyles(el, o.style);
5045         var cn = o.children || o.cn;
5046         if(cn){
5047             //http://bugs.kde.org/show_bug.cgi?id=71506
5048              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5049                 for(var i = 0, len = cn.length; i < len; i++) {
5050                     createDom(cn[i], el);
5051                 }
5052             }else{
5053                 createDom(cn, el);
5054             }
5055         }
5056         if(o.html){
5057             el.innerHTML = o.html;
5058         }
5059         if(parentNode){
5060            parentNode.appendChild(el);
5061         }
5062         return el;
5063     };
5064
5065     var ieTable = function(depth, s, h, e){
5066         tempTableEl.innerHTML = [s, h, e].join('');
5067         var i = -1, el = tempTableEl;
5068         while(++i < depth && el.firstChild){
5069             el = el.firstChild;
5070         }
5071         return el;
5072     };
5073
5074     // kill repeat to save bytes
5075     var ts = '<table>',
5076         te = '</table>',
5077         tbs = ts+'<tbody>',
5078         tbe = '</tbody>'+te,
5079         trs = tbs + '<tr>',
5080         tre = '</tr>'+tbe;
5081
5082     /**
5083      * @ignore
5084      * Nasty code for IE's broken table implementation
5085      */
5086     var insertIntoTable = function(tag, where, el, html){
5087         if(!tempTableEl){
5088             tempTableEl = document.createElement('div');
5089         }
5090         var node;
5091         var before = null;
5092         if(tag == 'td'){
5093             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5094                 return;
5095             }
5096             if(where == 'beforebegin'){
5097                 before = el;
5098                 el = el.parentNode;
5099             } else{
5100                 before = el.nextSibling;
5101                 el = el.parentNode;
5102             }
5103             node = ieTable(4, trs, html, tre);
5104         }
5105         else if(tag == 'tr'){
5106             if(where == 'beforebegin'){
5107                 before = el;
5108                 el = el.parentNode;
5109                 node = ieTable(3, tbs, html, tbe);
5110             } else if(where == 'afterend'){
5111                 before = el.nextSibling;
5112                 el = el.parentNode;
5113                 node = ieTable(3, tbs, html, tbe);
5114             } else{ // INTO a TR
5115                 if(where == 'afterbegin'){
5116                     before = el.firstChild;
5117                 }
5118                 node = ieTable(4, trs, html, tre);
5119             }
5120         } else if(tag == 'tbody'){
5121             if(where == 'beforebegin'){
5122                 before = el;
5123                 el = el.parentNode;
5124                 node = ieTable(2, ts, html, te);
5125             } else if(where == 'afterend'){
5126                 before = el.nextSibling;
5127                 el = el.parentNode;
5128                 node = ieTable(2, ts, html, te);
5129             } else{
5130                 if(where == 'afterbegin'){
5131                     before = el.firstChild;
5132                 }
5133                 node = ieTable(3, tbs, html, tbe);
5134             }
5135         } else{ // TABLE
5136             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5137                 return;
5138             }
5139             if(where == 'afterbegin'){
5140                 before = el.firstChild;
5141             }
5142             node = ieTable(2, ts, html, te);
5143         }
5144         el.insertBefore(node, before);
5145         return node;
5146     };
5147     
5148     // this is a bit like the react update code...
5149     // 
5150     
5151     var updateNode = function(from, to)
5152     {
5153         // should we handle non-standard elements?
5154         
5155         if (from.nodeType != to.nodeType) {
5156             from.parentNode.replaceChild(to, from);
5157         }
5158         
5159         if (from.nodeType == 3) {
5160             // assume it's text?!
5161             if (from.data == to.data) {
5162                 return;
5163             }
5164             from.data = to.data;
5165             return;
5166         }
5167         
5168         // assume 'to' doesnt have '1/3 nodetypes!
5169         if (from.nodeType !=1 || from.tagName != to.tagName) {
5170             from.parentNode.replaceChild(to, from);
5171             return;
5172         }
5173         // compare attributes
5174         var ar = Array.from(from.attributes);
5175         for(var i = 0; i< ar.length;i++) {
5176             if (to.hasAttribute(ar[i].name)) {
5177                 continue;
5178             }
5179             from.removeAttribute(ar[i].name);
5180         }
5181         ar = to.attributes;
5182         for(var i = 0; i< ar.length;i++) {
5183             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5184                 continue;
5185             }
5186             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5187         }
5188         // children
5189         var far = Array.from(from.childNodes);
5190         var tar = Array.from(to.childNodes);
5191         // if the lengths are different.. then it's probably a editable content change, rather than
5192         // a change of the block definition..
5193         if (from.innerHTML == to.innerHTML) {
5194             return;
5195         }
5196         if (far.length != tar.length) {
5197             from.innerHTML = to.innerHTML;
5198             return;
5199         }
5200         
5201         for(var i = 0; i < far.length; i++) {
5202             updateNode(far[i], tar[i]);
5203         }
5204         
5205         
5206     };
5207     
5208     
5209
5210     return {
5211         /** True to force the use of DOM instead of html fragments @type Boolean */
5212         useDom : false,
5213     
5214         /**
5215          * Returns the markup for the passed Element(s) config
5216          * @param {Object} o The Dom object spec (and children)
5217          * @return {String}
5218          */
5219         markup : function(o){
5220             return createHtml(o);
5221         },
5222     
5223         /**
5224          * Applies a style specification to an element
5225          * @param {String/HTMLElement} el The element to apply styles to
5226          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5227          * a function which returns such a specification.
5228          */
5229         applyStyles : function(el, styles){
5230             if(styles){
5231                el = Roo.fly(el);
5232                if(typeof styles == "string"){
5233                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5234                    var matches;
5235                    while ((matches = re.exec(styles)) != null){
5236                        el.setStyle(matches[1], matches[2]);
5237                    }
5238                }else if (typeof styles == "object"){
5239                    for (var style in styles){
5240                       el.setStyle(style, styles[style]);
5241                    }
5242                }else if (typeof styles == "function"){
5243                     Roo.DomHelper.applyStyles(el, styles.call());
5244                }
5245             }
5246         },
5247     
5248         /**
5249          * Inserts an HTML fragment into the Dom
5250          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5251          * @param {HTMLElement} el The context element
5252          * @param {String} html The HTML fragmenet
5253          * @return {HTMLElement} The new node
5254          */
5255         insertHtml : function(where, el, html){
5256             where = where.toLowerCase();
5257             if(el.insertAdjacentHTML){
5258                 if(tableRe.test(el.tagName)){
5259                     var rs;
5260                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5261                         return rs;
5262                     }
5263                 }
5264                 switch(where){
5265                     case "beforebegin":
5266                         el.insertAdjacentHTML('BeforeBegin', html);
5267                         return el.previousSibling;
5268                     case "afterbegin":
5269                         el.insertAdjacentHTML('AfterBegin', html);
5270                         return el.firstChild;
5271                     case "beforeend":
5272                         el.insertAdjacentHTML('BeforeEnd', html);
5273                         return el.lastChild;
5274                     case "afterend":
5275                         el.insertAdjacentHTML('AfterEnd', html);
5276                         return el.nextSibling;
5277                 }
5278                 throw 'Illegal insertion point -> "' + where + '"';
5279             }
5280             var range = el.ownerDocument.createRange();
5281             var frag;
5282             switch(where){
5283                  case "beforebegin":
5284                     range.setStartBefore(el);
5285                     frag = range.createContextualFragment(html);
5286                     el.parentNode.insertBefore(frag, el);
5287                     return el.previousSibling;
5288                  case "afterbegin":
5289                     if(el.firstChild){
5290                         range.setStartBefore(el.firstChild);
5291                         frag = range.createContextualFragment(html);
5292                         el.insertBefore(frag, el.firstChild);
5293                         return el.firstChild;
5294                     }else{
5295                         el.innerHTML = html;
5296                         return el.firstChild;
5297                     }
5298                 case "beforeend":
5299                     if(el.lastChild){
5300                         range.setStartAfter(el.lastChild);
5301                         frag = range.createContextualFragment(html);
5302                         el.appendChild(frag);
5303                         return el.lastChild;
5304                     }else{
5305                         el.innerHTML = html;
5306                         return el.lastChild;
5307                     }
5308                 case "afterend":
5309                     range.setStartAfter(el);
5310                     frag = range.createContextualFragment(html);
5311                     el.parentNode.insertBefore(frag, el.nextSibling);
5312                     return el.nextSibling;
5313                 }
5314                 throw 'Illegal insertion point -> "' + where + '"';
5315         },
5316     
5317         /**
5318          * Creates new Dom element(s) and inserts them before el
5319          * @param {String/HTMLElement/Element} el The context element
5320          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5321          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5322          * @return {HTMLElement/Roo.Element} The new node
5323          */
5324         insertBefore : function(el, o, returnElement){
5325             return this.doInsert(el, o, returnElement, "beforeBegin");
5326         },
5327     
5328         /**
5329          * Creates new Dom element(s) and inserts them after el
5330          * @param {String/HTMLElement/Element} el The context element
5331          * @param {Object} o The Dom object spec (and children)
5332          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5333          * @return {HTMLElement/Roo.Element} The new node
5334          */
5335         insertAfter : function(el, o, returnElement){
5336             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5337         },
5338     
5339         /**
5340          * Creates new Dom element(s) and inserts them as the first child of el
5341          * @param {String/HTMLElement/Element} el The context element
5342          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5343          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5344          * @return {HTMLElement/Roo.Element} The new node
5345          */
5346         insertFirst : function(el, o, returnElement){
5347             return this.doInsert(el, o, returnElement, "afterBegin");
5348         },
5349     
5350         // private
5351         doInsert : function(el, o, returnElement, pos, sibling){
5352             el = Roo.getDom(el);
5353             var newNode;
5354             if(this.useDom || o.ns){
5355                 newNode = createDom(o, null);
5356                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5357             }else{
5358                 var html = createHtml(o);
5359                 newNode = this.insertHtml(pos, el, html);
5360             }
5361             return returnElement ? Roo.get(newNode, true) : newNode;
5362         },
5363     
5364         /**
5365          * Creates new Dom element(s) and appends them to el
5366          * @param {String/HTMLElement/Element} el The context element
5367          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5368          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5369          * @return {HTMLElement/Roo.Element} The new node
5370          */
5371         append : function(el, o, returnElement){
5372             el = Roo.getDom(el);
5373             var newNode;
5374             if(this.useDom || o.ns){
5375                 newNode = createDom(o, null);
5376                 el.appendChild(newNode);
5377             }else{
5378                 var html = createHtml(o);
5379                 newNode = this.insertHtml("beforeEnd", el, html);
5380             }
5381             return returnElement ? Roo.get(newNode, true) : newNode;
5382         },
5383     
5384         /**
5385          * Creates new Dom element(s) and overwrites the contents of el with them
5386          * @param {String/HTMLElement/Element} el The context element
5387          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5388          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5389          * @return {HTMLElement/Roo.Element} The new node
5390          */
5391         overwrite : function(el, o, returnElement)
5392         {
5393             el = Roo.getDom(el);
5394             if (o.ns) {
5395               
5396                 while (el.childNodes.length) {
5397                     el.removeChild(el.firstChild);
5398                 }
5399                 createDom(o, el);
5400             } else {
5401                 el.innerHTML = createHtml(o);   
5402             }
5403             
5404             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5405         },
5406     
5407         /**
5408          * Creates a new Roo.DomHelper.Template from the Dom object spec
5409          * @param {Object} o The Dom object spec (and children)
5410          * @return {Roo.DomHelper.Template} The new template
5411          */
5412         createTemplate : function(o){
5413             var html = createHtml(o);
5414             return new Roo.Template(html);
5415         },
5416          /**
5417          * Updates the first element with the spec from the o (replacing if necessary)
5418          * This iterates through the children, and updates attributes / children etc..
5419          * @param {String/HTMLElement/Element} el The context element
5420          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5421          */
5422         
5423         update : function(el, o)
5424         {
5425             updateNode(Roo.getDom(el), createDom(o));
5426             
5427         }
5428         
5429         
5430     };
5431 }();
5432 /*
5433  * Based on:
5434  * Ext JS Library 1.1.1
5435  * Copyright(c) 2006-2007, Ext JS, LLC.
5436  *
5437  * Originally Released Under LGPL - original licence link has changed is not relivant.
5438  *
5439  * Fork - LGPL
5440  * <script type="text/javascript">
5441  */
5442  
5443 /**
5444 * @class Roo.Template
5445 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5446 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5447 * Usage:
5448 <pre><code>
5449 var t = new Roo.Template({
5450     html :  '&lt;div name="{id}"&gt;' + 
5451         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5452         '&lt;/div&gt;',
5453     myformat: function (value, allValues) {
5454         return 'XX' + value;
5455     }
5456 });
5457 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5458 </code></pre>
5459 * For more information see this blog post with examples:
5460 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5461      - Create Elements using DOM, HTML fragments and Templates</a>. 
5462 * @constructor
5463 * @param {Object} cfg - Configuration object.
5464 */
5465 Roo.Template = function(cfg){
5466     // BC!
5467     if(cfg instanceof Array){
5468         cfg = cfg.join("");
5469     }else if(arguments.length > 1){
5470         cfg = Array.prototype.join.call(arguments, "");
5471     }
5472     
5473     
5474     if (typeof(cfg) == 'object') {
5475         Roo.apply(this,cfg)
5476     } else {
5477         // bc
5478         this.html = cfg;
5479     }
5480     if (this.url) {
5481         this.load();
5482     }
5483     
5484 };
5485 Roo.Template.prototype = {
5486     
5487     /**
5488      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5489      */
5490     onLoad : false,
5491     
5492     
5493     /**
5494      * @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..
5495      *                    it should be fixed so that template is observable...
5496      */
5497     url : false,
5498     /**
5499      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5500      */
5501     html : '',
5502     
5503     
5504     compiled : false,
5505     loaded : false,
5506     /**
5507      * Returns an HTML fragment of this template with the specified values applied.
5508      * @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'})
5509      * @return {String} The HTML fragment
5510      */
5511     
5512    
5513     
5514     applyTemplate : function(values){
5515         //Roo.log(["applyTemplate", values]);
5516         try {
5517            
5518             if(this.compiled){
5519                 return this.compiled(values);
5520             }
5521             var useF = this.disableFormats !== true;
5522             var fm = Roo.util.Format, tpl = this;
5523             var fn = function(m, name, format, args){
5524                 if(format && useF){
5525                     if(format.substr(0, 5) == "this."){
5526                         return tpl.call(format.substr(5), values[name], values);
5527                     }else{
5528                         if(args){
5529                             // quoted values are required for strings in compiled templates, 
5530                             // but for non compiled we need to strip them
5531                             // quoted reversed for jsmin
5532                             var re = /^\s*['"](.*)["']\s*$/;
5533                             args = args.split(',');
5534                             for(var i = 0, len = args.length; i < len; i++){
5535                                 args[i] = args[i].replace(re, "$1");
5536                             }
5537                             args = [values[name]].concat(args);
5538                         }else{
5539                             args = [values[name]];
5540                         }
5541                         return fm[format].apply(fm, args);
5542                     }
5543                 }else{
5544                     return values[name] !== undefined ? values[name] : "";
5545                 }
5546             };
5547             return this.html.replace(this.re, fn);
5548         } catch (e) {
5549             Roo.log(e);
5550             throw e;
5551         }
5552          
5553     },
5554     
5555     loading : false,
5556       
5557     load : function ()
5558     {
5559          
5560         if (this.loading) {
5561             return;
5562         }
5563         var _t = this;
5564         
5565         this.loading = true;
5566         this.compiled = false;
5567         
5568         var cx = new Roo.data.Connection();
5569         cx.request({
5570             url : this.url,
5571             method : 'GET',
5572             success : function (response) {
5573                 _t.loading = false;
5574                 _t.url = false;
5575                 
5576                 _t.set(response.responseText,true);
5577                 _t.loaded = true;
5578                 if (_t.onLoad) {
5579                     _t.onLoad();
5580                 }
5581              },
5582             failure : function(response) {
5583                 Roo.log("Template failed to load from " + _t.url);
5584                 _t.loading = false;
5585             }
5586         });
5587     },
5588
5589     /**
5590      * Sets the HTML used as the template and optionally compiles it.
5591      * @param {String} html
5592      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5593      * @return {Roo.Template} this
5594      */
5595     set : function(html, compile){
5596         this.html = html;
5597         this.compiled = false;
5598         if(compile){
5599             this.compile();
5600         }
5601         return this;
5602     },
5603     
5604     /**
5605      * True to disable format functions (defaults to false)
5606      * @type Boolean
5607      */
5608     disableFormats : false,
5609     
5610     /**
5611     * The regular expression used to match template variables 
5612     * @type RegExp
5613     * @property 
5614     */
5615     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5616     
5617     /**
5618      * Compiles the template into an internal function, eliminating the RegEx overhead.
5619      * @return {Roo.Template} this
5620      */
5621     compile : function(){
5622         var fm = Roo.util.Format;
5623         var useF = this.disableFormats !== true;
5624         var sep = Roo.isGecko ? "+" : ",";
5625         var fn = function(m, name, format, args){
5626             if(format && useF){
5627                 args = args ? ',' + args : "";
5628                 if(format.substr(0, 5) != "this."){
5629                     format = "fm." + format + '(';
5630                 }else{
5631                     format = 'this.call("'+ format.substr(5) + '", ';
5632                     args = ", values";
5633                 }
5634             }else{
5635                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5636             }
5637             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5638         };
5639         var body;
5640         // branched to use + in gecko and [].join() in others
5641         if(Roo.isGecko){
5642             body = "this.compiled = function(values){ return '" +
5643                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5644                     "';};";
5645         }else{
5646             body = ["this.compiled = function(values){ return ['"];
5647             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5648             body.push("'].join('');};");
5649             body = body.join('');
5650         }
5651         /**
5652          * eval:var:values
5653          * eval:var:fm
5654          */
5655         eval(body);
5656         return this;
5657     },
5658     
5659     // private function used to call members
5660     call : function(fnName, value, allValues){
5661         return this[fnName](value, allValues);
5662     },
5663     
5664     /**
5665      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5666      * @param {String/HTMLElement/Roo.Element} el The context element
5667      * @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'})
5668      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5669      * @return {HTMLElement/Roo.Element} The new node or Element
5670      */
5671     insertFirst: function(el, values, returnElement){
5672         return this.doInsert('afterBegin', el, values, returnElement);
5673     },
5674
5675     /**
5676      * Applies the supplied values to the template and inserts the new node(s) before el.
5677      * @param {String/HTMLElement/Roo.Element} el The context element
5678      * @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'})
5679      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5680      * @return {HTMLElement/Roo.Element} The new node or Element
5681      */
5682     insertBefore: function(el, values, returnElement){
5683         return this.doInsert('beforeBegin', el, values, returnElement);
5684     },
5685
5686     /**
5687      * Applies the supplied values to the template and inserts the new node(s) after el.
5688      * @param {String/HTMLElement/Roo.Element} el The context element
5689      * @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'})
5690      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5691      * @return {HTMLElement/Roo.Element} The new node or Element
5692      */
5693     insertAfter : function(el, values, returnElement){
5694         return this.doInsert('afterEnd', el, values, returnElement);
5695     },
5696     
5697     /**
5698      * Applies the supplied values to the template and appends the new node(s) to el.
5699      * @param {String/HTMLElement/Roo.Element} el The context element
5700      * @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'})
5701      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5702      * @return {HTMLElement/Roo.Element} The new node or Element
5703      */
5704     append : function(el, values, returnElement){
5705         return this.doInsert('beforeEnd', el, values, returnElement);
5706     },
5707
5708     doInsert : function(where, el, values, returnEl){
5709         el = Roo.getDom(el);
5710         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5711         return returnEl ? Roo.get(newNode, true) : newNode;
5712     },
5713
5714     /**
5715      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5716      * @param {String/HTMLElement/Roo.Element} el The context element
5717      * @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'})
5718      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5719      * @return {HTMLElement/Roo.Element} The new node or Element
5720      */
5721     overwrite : function(el, values, returnElement){
5722         el = Roo.getDom(el);
5723         el.innerHTML = this.applyTemplate(values);
5724         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5725     }
5726 };
5727 /**
5728  * Alias for {@link #applyTemplate}
5729  * @method
5730  */
5731 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5732
5733 // backwards compat
5734 Roo.DomHelper.Template = Roo.Template;
5735
5736 /**
5737  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5738  * @param {String/HTMLElement} el A DOM element or its id
5739  * @returns {Roo.Template} The created template
5740  * @static
5741  */
5742 Roo.Template.from = function(el){
5743     el = Roo.getDom(el);
5744     return new Roo.Template(el.value || el.innerHTML);
5745 };/*
5746  * Based on:
5747  * Ext JS Library 1.1.1
5748  * Copyright(c) 2006-2007, Ext JS, LLC.
5749  *
5750  * Originally Released Under LGPL - original licence link has changed is not relivant.
5751  *
5752  * Fork - LGPL
5753  * <script type="text/javascript">
5754  */
5755  
5756
5757 /*
5758  * This is code is also distributed under MIT license for use
5759  * with jQuery and prototype JavaScript libraries.
5760  */
5761 /**
5762  * @class Roo.DomQuery
5763 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).
5764 <p>
5765 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>
5766
5767 <p>
5768 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.
5769 </p>
5770 <h4>Element Selectors:</h4>
5771 <ul class="list">
5772     <li> <b>*</b> any element</li>
5773     <li> <b>E</b> an element with the tag E</li>
5774     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5775     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5776     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5777     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5778 </ul>
5779 <h4>Attribute Selectors:</h4>
5780 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5781 <ul class="list">
5782     <li> <b>E[foo]</b> has an attribute "foo"</li>
5783     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5784     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5785     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5786     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5787     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5788     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5789 </ul>
5790 <h4>Pseudo Classes:</h4>
5791 <ul class="list">
5792     <li> <b>E:first-child</b> E is the first child of its parent</li>
5793     <li> <b>E:last-child</b> E is the last child of its parent</li>
5794     <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>
5795     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5796     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5797     <li> <b>E:only-child</b> E is the only child of its parent</li>
5798     <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>
5799     <li> <b>E:first</b> the first E in the resultset</li>
5800     <li> <b>E:last</b> the last E in the resultset</li>
5801     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5802     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5803     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5804     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5805     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5806     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5807     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5808     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5809     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5810 </ul>
5811 <h4>CSS Value Selectors:</h4>
5812 <ul class="list">
5813     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5814     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5815     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5816     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5817     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5818     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5819 </ul>
5820  * @static
5821  */
5822 Roo.DomQuery = function(){
5823     var cache = {}, simpleCache = {}, valueCache = {};
5824     var nonSpace = /\S/;
5825     var trimRe = /^\s+|\s+$/g;
5826     var tplRe = /\{(\d+)\}/g;
5827     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5828     var tagTokenRe = /^(#)?([\w-\*]+)/;
5829     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5830
5831     function child(p, index){
5832         var i = 0;
5833         var n = p.firstChild;
5834         while(n){
5835             if(n.nodeType == 1){
5836                if(++i == index){
5837                    return n;
5838                }
5839             }
5840             n = n.nextSibling;
5841         }
5842         return null;
5843     };
5844
5845     function next(n){
5846         while((n = n.nextSibling) && n.nodeType != 1);
5847         return n;
5848     };
5849
5850     function prev(n){
5851         while((n = n.previousSibling) && n.nodeType != 1);
5852         return n;
5853     };
5854
5855     function children(d){
5856         var n = d.firstChild, ni = -1;
5857             while(n){
5858                 var nx = n.nextSibling;
5859                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5860                     d.removeChild(n);
5861                 }else{
5862                     n.nodeIndex = ++ni;
5863                 }
5864                 n = nx;
5865             }
5866             return this;
5867         };
5868
5869     function byClassName(c, a, v){
5870         if(!v){
5871             return c;
5872         }
5873         var r = [], ri = -1, cn;
5874         for(var i = 0, ci; ci = c[i]; i++){
5875             
5876             
5877             if((' '+
5878                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5879                  +' ').indexOf(v) != -1){
5880                 r[++ri] = ci;
5881             }
5882         }
5883         return r;
5884     };
5885
5886     function attrValue(n, attr){
5887         if(!n.tagName && typeof n.length != "undefined"){
5888             n = n[0];
5889         }
5890         if(!n){
5891             return null;
5892         }
5893         if(attr == "for"){
5894             return n.htmlFor;
5895         }
5896         if(attr == "class" || attr == "className"){
5897             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5898         }
5899         return n.getAttribute(attr) || n[attr];
5900
5901     };
5902
5903     function getNodes(ns, mode, tagName){
5904         var result = [], ri = -1, cs;
5905         if(!ns){
5906             return result;
5907         }
5908         tagName = tagName || "*";
5909         if(typeof ns.getElementsByTagName != "undefined"){
5910             ns = [ns];
5911         }
5912         if(!mode){
5913             for(var i = 0, ni; ni = ns[i]; i++){
5914                 cs = ni.getElementsByTagName(tagName);
5915                 for(var j = 0, ci; ci = cs[j]; j++){
5916                     result[++ri] = ci;
5917                 }
5918             }
5919         }else if(mode == "/" || mode == ">"){
5920             var utag = tagName.toUpperCase();
5921             for(var i = 0, ni, cn; ni = ns[i]; i++){
5922                 cn = ni.children || ni.childNodes;
5923                 for(var j = 0, cj; cj = cn[j]; j++){
5924                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5925                         result[++ri] = cj;
5926                     }
5927                 }
5928             }
5929         }else if(mode == "+"){
5930             var utag = tagName.toUpperCase();
5931             for(var i = 0, n; n = ns[i]; i++){
5932                 while((n = n.nextSibling) && n.nodeType != 1);
5933                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5934                     result[++ri] = n;
5935                 }
5936             }
5937         }else if(mode == "~"){
5938             for(var i = 0, n; n = ns[i]; i++){
5939                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5940                 if(n){
5941                     result[++ri] = n;
5942                 }
5943             }
5944         }
5945         return result;
5946     };
5947
5948     function concat(a, b){
5949         if(b.slice){
5950             return a.concat(b);
5951         }
5952         for(var i = 0, l = b.length; i < l; i++){
5953             a[a.length] = b[i];
5954         }
5955         return a;
5956     }
5957
5958     function byTag(cs, tagName){
5959         if(cs.tagName || cs == document){
5960             cs = [cs];
5961         }
5962         if(!tagName){
5963             return cs;
5964         }
5965         var r = [], ri = -1;
5966         tagName = tagName.toLowerCase();
5967         for(var i = 0, ci; ci = cs[i]; i++){
5968             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5969                 r[++ri] = ci;
5970             }
5971         }
5972         return r;
5973     };
5974
5975     function byId(cs, attr, id){
5976         if(cs.tagName || cs == document){
5977             cs = [cs];
5978         }
5979         if(!id){
5980             return cs;
5981         }
5982         var r = [], ri = -1;
5983         for(var i = 0,ci; ci = cs[i]; i++){
5984             if(ci && ci.id == id){
5985                 r[++ri] = ci;
5986                 return r;
5987             }
5988         }
5989         return r;
5990     };
5991
5992     function byAttribute(cs, attr, value, op, custom){
5993         var r = [], ri = -1, st = custom=="{";
5994         var f = Roo.DomQuery.operators[op];
5995         for(var i = 0, ci; ci = cs[i]; i++){
5996             var a;
5997             if(st){
5998                 a = Roo.DomQuery.getStyle(ci, attr);
5999             }
6000             else if(attr == "class" || attr == "className"){
6001                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6002             }else if(attr == "for"){
6003                 a = ci.htmlFor;
6004             }else if(attr == "href"){
6005                 a = ci.getAttribute("href", 2);
6006             }else{
6007                 a = ci.getAttribute(attr);
6008             }
6009             if((f && f(a, value)) || (!f && a)){
6010                 r[++ri] = ci;
6011             }
6012         }
6013         return r;
6014     };
6015
6016     function byPseudo(cs, name, value){
6017         return Roo.DomQuery.pseudos[name](cs, value);
6018     };
6019
6020     // This is for IE MSXML which does not support expandos.
6021     // IE runs the same speed using setAttribute, however FF slows way down
6022     // and Safari completely fails so they need to continue to use expandos.
6023     var isIE = window.ActiveXObject ? true : false;
6024
6025     // this eval is stop the compressor from
6026     // renaming the variable to something shorter
6027     
6028     /** eval:var:batch */
6029     var batch = 30803; 
6030
6031     var key = 30803;
6032
6033     function nodupIEXml(cs){
6034         var d = ++key;
6035         cs[0].setAttribute("_nodup", d);
6036         var r = [cs[0]];
6037         for(var i = 1, len = cs.length; i < len; i++){
6038             var c = cs[i];
6039             if(!c.getAttribute("_nodup") != d){
6040                 c.setAttribute("_nodup", d);
6041                 r[r.length] = c;
6042             }
6043         }
6044         for(var i = 0, len = cs.length; i < len; i++){
6045             cs[i].removeAttribute("_nodup");
6046         }
6047         return r;
6048     }
6049
6050     function nodup(cs){
6051         if(!cs){
6052             return [];
6053         }
6054         var len = cs.length, c, i, r = cs, cj, ri = -1;
6055         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6056             return cs;
6057         }
6058         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6059             return nodupIEXml(cs);
6060         }
6061         var d = ++key;
6062         cs[0]._nodup = d;
6063         for(i = 1; c = cs[i]; i++){
6064             if(c._nodup != d){
6065                 c._nodup = d;
6066             }else{
6067                 r = [];
6068                 for(var j = 0; j < i; j++){
6069                     r[++ri] = cs[j];
6070                 }
6071                 for(j = i+1; cj = cs[j]; j++){
6072                     if(cj._nodup != d){
6073                         cj._nodup = d;
6074                         r[++ri] = cj;
6075                     }
6076                 }
6077                 return r;
6078             }
6079         }
6080         return r;
6081     }
6082
6083     function quickDiffIEXml(c1, c2){
6084         var d = ++key;
6085         for(var i = 0, len = c1.length; i < len; i++){
6086             c1[i].setAttribute("_qdiff", d);
6087         }
6088         var r = [];
6089         for(var i = 0, len = c2.length; i < len; i++){
6090             if(c2[i].getAttribute("_qdiff") != d){
6091                 r[r.length] = c2[i];
6092             }
6093         }
6094         for(var i = 0, len = c1.length; i < len; i++){
6095            c1[i].removeAttribute("_qdiff");
6096         }
6097         return r;
6098     }
6099
6100     function quickDiff(c1, c2){
6101         var len1 = c1.length;
6102         if(!len1){
6103             return c2;
6104         }
6105         if(isIE && c1[0].selectSingleNode){
6106             return quickDiffIEXml(c1, c2);
6107         }
6108         var d = ++key;
6109         for(var i = 0; i < len1; i++){
6110             c1[i]._qdiff = d;
6111         }
6112         var r = [];
6113         for(var i = 0, len = c2.length; i < len; i++){
6114             if(c2[i]._qdiff != d){
6115                 r[r.length] = c2[i];
6116             }
6117         }
6118         return r;
6119     }
6120
6121     function quickId(ns, mode, root, id){
6122         if(ns == root){
6123            var d = root.ownerDocument || root;
6124            return d.getElementById(id);
6125         }
6126         ns = getNodes(ns, mode, "*");
6127         return byId(ns, null, id);
6128     }
6129
6130     return {
6131         getStyle : function(el, name){
6132             return Roo.fly(el).getStyle(name);
6133         },
6134         /**
6135          * Compiles a selector/xpath query into a reusable function. The returned function
6136          * takes one parameter "root" (optional), which is the context node from where the query should start.
6137          * @param {String} selector The selector/xpath query
6138          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6139          * @return {Function}
6140          */
6141         compile : function(path, type){
6142             type = type || "select";
6143             
6144             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6145             var q = path, mode, lq;
6146             var tk = Roo.DomQuery.matchers;
6147             var tklen = tk.length;
6148             var mm;
6149
6150             // accept leading mode switch
6151             var lmode = q.match(modeRe);
6152             if(lmode && lmode[1]){
6153                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6154                 q = q.replace(lmode[1], "");
6155             }
6156             // strip leading slashes
6157             while(path.substr(0, 1)=="/"){
6158                 path = path.substr(1);
6159             }
6160
6161             while(q && lq != q){
6162                 lq = q;
6163                 var tm = q.match(tagTokenRe);
6164                 if(type == "select"){
6165                     if(tm){
6166                         if(tm[1] == "#"){
6167                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6168                         }else{
6169                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6170                         }
6171                         q = q.replace(tm[0], "");
6172                     }else if(q.substr(0, 1) != '@'){
6173                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6174                     }
6175                 }else{
6176                     if(tm){
6177                         if(tm[1] == "#"){
6178                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6179                         }else{
6180                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6181                         }
6182                         q = q.replace(tm[0], "");
6183                     }
6184                 }
6185                 while(!(mm = q.match(modeRe))){
6186                     var matched = false;
6187                     for(var j = 0; j < tklen; j++){
6188                         var t = tk[j];
6189                         var m = q.match(t.re);
6190                         if(m){
6191                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6192                                                     return m[i];
6193                                                 });
6194                             q = q.replace(m[0], "");
6195                             matched = true;
6196                             break;
6197                         }
6198                     }
6199                     // prevent infinite loop on bad selector
6200                     if(!matched){
6201                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6202                     }
6203                 }
6204                 if(mm[1]){
6205                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6206                     q = q.replace(mm[1], "");
6207                 }
6208             }
6209             fn[fn.length] = "return nodup(n);\n}";
6210             
6211              /** 
6212               * list of variables that need from compression as they are used by eval.
6213              *  eval:var:batch 
6214              *  eval:var:nodup
6215              *  eval:var:byTag
6216              *  eval:var:ById
6217              *  eval:var:getNodes
6218              *  eval:var:quickId
6219              *  eval:var:mode
6220              *  eval:var:root
6221              *  eval:var:n
6222              *  eval:var:byClassName
6223              *  eval:var:byPseudo
6224              *  eval:var:byAttribute
6225              *  eval:var:attrValue
6226              * 
6227              **/ 
6228             eval(fn.join(""));
6229             return f;
6230         },
6231
6232         /**
6233          * Selects a group of elements.
6234          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6235          * @param {Node} root (optional) The start of the query (defaults to document).
6236          * @return {Array}
6237          */
6238         select : function(path, root, type){
6239             if(!root || root == document){
6240                 root = document;
6241             }
6242             if(typeof root == "string"){
6243                 root = document.getElementById(root);
6244             }
6245             var paths = path.split(",");
6246             var results = [];
6247             for(var i = 0, len = paths.length; i < len; i++){
6248                 var p = paths[i].replace(trimRe, "");
6249                 if(!cache[p]){
6250                     cache[p] = Roo.DomQuery.compile(p);
6251                     if(!cache[p]){
6252                         throw p + " is not a valid selector";
6253                     }
6254                 }
6255                 var result = cache[p](root);
6256                 if(result && result != document){
6257                     results = results.concat(result);
6258                 }
6259             }
6260             if(paths.length > 1){
6261                 return nodup(results);
6262             }
6263             return results;
6264         },
6265
6266         /**
6267          * Selects a single element.
6268          * @param {String} selector The selector/xpath query
6269          * @param {Node} root (optional) The start of the query (defaults to document).
6270          * @return {Element}
6271          */
6272         selectNode : function(path, root){
6273             return Roo.DomQuery.select(path, root)[0];
6274         },
6275
6276         /**
6277          * Selects the value of a node, optionally replacing null with the defaultValue.
6278          * @param {String} selector The selector/xpath query
6279          * @param {Node} root (optional) The start of the query (defaults to document).
6280          * @param {String} defaultValue
6281          */
6282         selectValue : function(path, root, defaultValue){
6283             path = path.replace(trimRe, "");
6284             if(!valueCache[path]){
6285                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6286             }
6287             var n = valueCache[path](root);
6288             n = n[0] ? n[0] : n;
6289             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6290             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6291         },
6292
6293         /**
6294          * Selects the value of a node, parsing integers and floats.
6295          * @param {String} selector The selector/xpath query
6296          * @param {Node} root (optional) The start of the query (defaults to document).
6297          * @param {Number} defaultValue
6298          * @return {Number}
6299          */
6300         selectNumber : function(path, root, defaultValue){
6301             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6302             return parseFloat(v);
6303         },
6304
6305         /**
6306          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6307          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6308          * @param {String} selector The simple selector to test
6309          * @return {Boolean}
6310          */
6311         is : function(el, ss){
6312             if(typeof el == "string"){
6313                 el = document.getElementById(el);
6314             }
6315             var isArray = (el instanceof Array);
6316             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6317             return isArray ? (result.length == el.length) : (result.length > 0);
6318         },
6319
6320         /**
6321          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6322          * @param {Array} el An array of elements to filter
6323          * @param {String} selector The simple selector to test
6324          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6325          * the selector instead of the ones that match
6326          * @return {Array}
6327          */
6328         filter : function(els, ss, nonMatches){
6329             ss = ss.replace(trimRe, "");
6330             if(!simpleCache[ss]){
6331                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6332             }
6333             var result = simpleCache[ss](els);
6334             return nonMatches ? quickDiff(result, els) : result;
6335         },
6336
6337         /**
6338          * Collection of matching regular expressions and code snippets.
6339          */
6340         matchers : [{
6341                 re: /^\.([\w-]+)/,
6342                 select: 'n = byClassName(n, null, " {1} ");'
6343             }, {
6344                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6345                 select: 'n = byPseudo(n, "{1}", "{2}");'
6346             },{
6347                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6348                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6349             }, {
6350                 re: /^#([\w-]+)/,
6351                 select: 'n = byId(n, null, "{1}");'
6352             },{
6353                 re: /^@([\w-]+)/,
6354                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6355             }
6356         ],
6357
6358         /**
6359          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6360          * 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;.
6361          */
6362         operators : {
6363             "=" : function(a, v){
6364                 return a == v;
6365             },
6366             "!=" : function(a, v){
6367                 return a != v;
6368             },
6369             "^=" : function(a, v){
6370                 return a && a.substr(0, v.length) == v;
6371             },
6372             "$=" : function(a, v){
6373                 return a && a.substr(a.length-v.length) == v;
6374             },
6375             "*=" : function(a, v){
6376                 return a && a.indexOf(v) !== -1;
6377             },
6378             "%=" : function(a, v){
6379                 return (a % v) == 0;
6380             },
6381             "|=" : function(a, v){
6382                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6383             },
6384             "~=" : function(a, v){
6385                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6386             }
6387         },
6388
6389         /**
6390          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6391          * and the argument (if any) supplied in the selector.
6392          */
6393         pseudos : {
6394             "first-child" : function(c){
6395                 var r = [], ri = -1, n;
6396                 for(var i = 0, ci; ci = n = c[i]; i++){
6397                     while((n = n.previousSibling) && n.nodeType != 1);
6398                     if(!n){
6399                         r[++ri] = ci;
6400                     }
6401                 }
6402                 return r;
6403             },
6404
6405             "last-child" : function(c){
6406                 var r = [], ri = -1, n;
6407                 for(var i = 0, ci; ci = n = c[i]; i++){
6408                     while((n = n.nextSibling) && n.nodeType != 1);
6409                     if(!n){
6410                         r[++ri] = ci;
6411                     }
6412                 }
6413                 return r;
6414             },
6415
6416             "nth-child" : function(c, a) {
6417                 var r = [], ri = -1;
6418                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6419                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6420                 for(var i = 0, n; n = c[i]; i++){
6421                     var pn = n.parentNode;
6422                     if (batch != pn._batch) {
6423                         var j = 0;
6424                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6425                             if(cn.nodeType == 1){
6426                                cn.nodeIndex = ++j;
6427                             }
6428                         }
6429                         pn._batch = batch;
6430                     }
6431                     if (f == 1) {
6432                         if (l == 0 || n.nodeIndex == l){
6433                             r[++ri] = n;
6434                         }
6435                     } else if ((n.nodeIndex + l) % f == 0){
6436                         r[++ri] = n;
6437                     }
6438                 }
6439
6440                 return r;
6441             },
6442
6443             "only-child" : function(c){
6444                 var r = [], ri = -1;;
6445                 for(var i = 0, ci; ci = c[i]; i++){
6446                     if(!prev(ci) && !next(ci)){
6447                         r[++ri] = ci;
6448                     }
6449                 }
6450                 return r;
6451             },
6452
6453             "empty" : function(c){
6454                 var r = [], ri = -1;
6455                 for(var i = 0, ci; ci = c[i]; i++){
6456                     var cns = ci.childNodes, j = 0, cn, empty = true;
6457                     while(cn = cns[j]){
6458                         ++j;
6459                         if(cn.nodeType == 1 || cn.nodeType == 3){
6460                             empty = false;
6461                             break;
6462                         }
6463                     }
6464                     if(empty){
6465                         r[++ri] = ci;
6466                     }
6467                 }
6468                 return r;
6469             },
6470
6471             "contains" : function(c, v){
6472                 var r = [], ri = -1;
6473                 for(var i = 0, ci; ci = c[i]; i++){
6474                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6475                         r[++ri] = ci;
6476                     }
6477                 }
6478                 return r;
6479             },
6480
6481             "nodeValue" : function(c, v){
6482                 var r = [], ri = -1;
6483                 for(var i = 0, ci; ci = c[i]; i++){
6484                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6485                         r[++ri] = ci;
6486                     }
6487                 }
6488                 return r;
6489             },
6490
6491             "checked" : function(c){
6492                 var r = [], ri = -1;
6493                 for(var i = 0, ci; ci = c[i]; i++){
6494                     if(ci.checked == true){
6495                         r[++ri] = ci;
6496                     }
6497                 }
6498                 return r;
6499             },
6500
6501             "not" : function(c, ss){
6502                 return Roo.DomQuery.filter(c, ss, true);
6503             },
6504
6505             "odd" : function(c){
6506                 return this["nth-child"](c, "odd");
6507             },
6508
6509             "even" : function(c){
6510                 return this["nth-child"](c, "even");
6511             },
6512
6513             "nth" : function(c, a){
6514                 return c[a-1] || [];
6515             },
6516
6517             "first" : function(c){
6518                 return c[0] || [];
6519             },
6520
6521             "last" : function(c){
6522                 return c[c.length-1] || [];
6523             },
6524
6525             "has" : function(c, ss){
6526                 var s = Roo.DomQuery.select;
6527                 var r = [], ri = -1;
6528                 for(var i = 0, ci; ci = c[i]; i++){
6529                     if(s(ss, ci).length > 0){
6530                         r[++ri] = ci;
6531                     }
6532                 }
6533                 return r;
6534             },
6535
6536             "next" : function(c, ss){
6537                 var is = Roo.DomQuery.is;
6538                 var r = [], ri = -1;
6539                 for(var i = 0, ci; ci = c[i]; i++){
6540                     var n = next(ci);
6541                     if(n && is(n, ss)){
6542                         r[++ri] = ci;
6543                     }
6544                 }
6545                 return r;
6546             },
6547
6548             "prev" : function(c, ss){
6549                 var is = Roo.DomQuery.is;
6550                 var r = [], ri = -1;
6551                 for(var i = 0, ci; ci = c[i]; i++){
6552                     var n = prev(ci);
6553                     if(n && is(n, ss)){
6554                         r[++ri] = ci;
6555                     }
6556                 }
6557                 return r;
6558             }
6559         }
6560     };
6561 }();
6562
6563 /**
6564  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6565  * @param {String} path The selector/xpath query
6566  * @param {Node} root (optional) The start of the query (defaults to document).
6567  * @return {Array}
6568  * @member Roo
6569  * @method query
6570  */
6571 Roo.query = Roo.DomQuery.select;
6572 /*
6573  * Based on:
6574  * Ext JS Library 1.1.1
6575  * Copyright(c) 2006-2007, Ext JS, LLC.
6576  *
6577  * Originally Released Under LGPL - original licence link has changed is not relivant.
6578  *
6579  * Fork - LGPL
6580  * <script type="text/javascript">
6581  */
6582
6583 /**
6584  * @class Roo.util.Observable
6585  * Base class that provides a common interface for publishing events. Subclasses are expected to
6586  * to have a property "events" with all the events defined.<br>
6587  * For example:
6588  * <pre><code>
6589  Employee = function(name){
6590     this.name = name;
6591     this.addEvents({
6592         "fired" : true,
6593         "quit" : true
6594     });
6595  }
6596  Roo.extend(Employee, Roo.util.Observable);
6597 </code></pre>
6598  * @param {Object} config properties to use (incuding events / listeners)
6599  */
6600
6601 Roo.util.Observable = function(cfg){
6602     
6603     cfg = cfg|| {};
6604     this.addEvents(cfg.events || {});
6605     if (cfg.events) {
6606         delete cfg.events; // make sure
6607     }
6608      
6609     Roo.apply(this, cfg);
6610     
6611     if(this.listeners){
6612         this.on(this.listeners);
6613         delete this.listeners;
6614     }
6615 };
6616 Roo.util.Observable.prototype = {
6617     /** 
6618  * @cfg {Object} listeners  list of events and functions to call for this object, 
6619  * For example :
6620  * <pre><code>
6621     listeners :  { 
6622        'click' : function(e) {
6623            ..... 
6624         } ,
6625         .... 
6626     } 
6627   </code></pre>
6628  */
6629     
6630     
6631     /**
6632      * Fires the specified event with the passed parameters (minus the event name).
6633      * @param {String} eventName
6634      * @param {Object...} args Variable number of parameters are passed to handlers
6635      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6636      */
6637     fireEvent : function(){
6638         var ce = this.events[arguments[0].toLowerCase()];
6639         if(typeof ce == "object"){
6640             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6641         }else{
6642             return true;
6643         }
6644     },
6645
6646     // private
6647     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6648
6649     /**
6650      * Appends an event handler to this component
6651      * @param {String}   eventName The type of event to listen for
6652      * @param {Function} handler The method the event invokes
6653      * @param {Object}   scope (optional) The scope in which to execute the handler
6654      * function. The handler function's "this" context.
6655      * @param {Object}   options (optional) An object containing handler configuration
6656      * properties. This may contain any of the following properties:<ul>
6657      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6658      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6659      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6660      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6661      * by the specified number of milliseconds. If the event fires again within that time, the original
6662      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6663      * </ul><br>
6664      * <p>
6665      * <b>Combining Options</b><br>
6666      * Using the options argument, it is possible to combine different types of listeners:<br>
6667      * <br>
6668      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6669                 <pre><code>
6670                 el.on('click', this.onClick, this, {
6671                         single: true,
6672                 delay: 100,
6673                 forumId: 4
6674                 });
6675                 </code></pre>
6676      * <p>
6677      * <b>Attaching multiple handlers in 1 call</b><br>
6678      * The method also allows for a single argument to be passed which is a config object containing properties
6679      * which specify multiple handlers.
6680      * <pre><code>
6681                 el.on({
6682                         'click': {
6683                         fn: this.onClick,
6684                         scope: this,
6685                         delay: 100
6686                 }, 
6687                 'mouseover': {
6688                         fn: this.onMouseOver,
6689                         scope: this
6690                 },
6691                 'mouseout': {
6692                         fn: this.onMouseOut,
6693                         scope: this
6694                 }
6695                 });
6696                 </code></pre>
6697      * <p>
6698      * Or a shorthand syntax which passes the same scope object to all handlers:
6699         <pre><code>
6700                 el.on({
6701                         'click': this.onClick,
6702                 'mouseover': this.onMouseOver,
6703                 'mouseout': this.onMouseOut,
6704                 scope: this
6705                 });
6706                 </code></pre>
6707      */
6708     addListener : function(eventName, fn, scope, o){
6709         if(typeof eventName == "object"){
6710             o = eventName;
6711             for(var e in o){
6712                 if(this.filterOptRe.test(e)){
6713                     continue;
6714                 }
6715                 if(typeof o[e] == "function"){
6716                     // shared options
6717                     this.addListener(e, o[e], o.scope,  o);
6718                 }else{
6719                     // individual options
6720                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6721                 }
6722             }
6723             return;
6724         }
6725         o = (!o || typeof o == "boolean") ? {} : o;
6726         eventName = eventName.toLowerCase();
6727         var ce = this.events[eventName] || true;
6728         if(typeof ce == "boolean"){
6729             ce = new Roo.util.Event(this, eventName);
6730             this.events[eventName] = ce;
6731         }
6732         ce.addListener(fn, scope, o);
6733     },
6734
6735     /**
6736      * Removes a listener
6737      * @param {String}   eventName     The type of event to listen for
6738      * @param {Function} handler        The handler to remove
6739      * @param {Object}   scope  (optional) The scope (this object) for the handler
6740      */
6741     removeListener : function(eventName, fn, scope){
6742         var ce = this.events[eventName.toLowerCase()];
6743         if(typeof ce == "object"){
6744             ce.removeListener(fn, scope);
6745         }
6746     },
6747
6748     /**
6749      * Removes all listeners for this object
6750      */
6751     purgeListeners : function(){
6752         for(var evt in this.events){
6753             if(typeof this.events[evt] == "object"){
6754                  this.events[evt].clearListeners();
6755             }
6756         }
6757     },
6758
6759     relayEvents : function(o, events){
6760         var createHandler = function(ename){
6761             return function(){
6762                  
6763                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6764             };
6765         };
6766         for(var i = 0, len = events.length; i < len; i++){
6767             var ename = events[i];
6768             if(!this.events[ename]){
6769                 this.events[ename] = true;
6770             };
6771             o.on(ename, createHandler(ename), this);
6772         }
6773     },
6774
6775     /**
6776      * Used to define events on this Observable
6777      * @param {Object} object The object with the events defined
6778      */
6779     addEvents : function(o){
6780         if(!this.events){
6781             this.events = {};
6782         }
6783         Roo.applyIf(this.events, o);
6784     },
6785
6786     /**
6787      * Checks to see if this object has any listeners for a specified event
6788      * @param {String} eventName The name of the event to check for
6789      * @return {Boolean} True if the event is being listened for, else false
6790      */
6791     hasListener : function(eventName){
6792         var e = this.events[eventName];
6793         return typeof e == "object" && e.listeners.length > 0;
6794     }
6795 };
6796 /**
6797  * Appends an event handler to this element (shorthand for addListener)
6798  * @param {String}   eventName     The type of event to listen for
6799  * @param {Function} handler        The method the event invokes
6800  * @param {Object}   scope (optional) The scope in which to execute the handler
6801  * function. The handler function's "this" context.
6802  * @param {Object}   options  (optional)
6803  * @method
6804  */
6805 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6806 /**
6807  * Removes a listener (shorthand for removeListener)
6808  * @param {String}   eventName     The type of event to listen for
6809  * @param {Function} handler        The handler to remove
6810  * @param {Object}   scope  (optional) The scope (this object) for the handler
6811  * @method
6812  */
6813 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6814
6815 /**
6816  * Starts capture on the specified Observable. All events will be passed
6817  * to the supplied function with the event name + standard signature of the event
6818  * <b>before</b> the event is fired. If the supplied function returns false,
6819  * the event will not fire.
6820  * @param {Observable} o The Observable to capture
6821  * @param {Function} fn The function to call
6822  * @param {Object} scope (optional) The scope (this object) for the fn
6823  * @static
6824  */
6825 Roo.util.Observable.capture = function(o, fn, scope){
6826     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6827 };
6828
6829 /**
6830  * Removes <b>all</b> added captures from the Observable.
6831  * @param {Observable} o The Observable to release
6832  * @static
6833  */
6834 Roo.util.Observable.releaseCapture = function(o){
6835     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6836 };
6837
6838 (function(){
6839
6840     var createBuffered = function(h, o, scope){
6841         var task = new Roo.util.DelayedTask();
6842         return function(){
6843             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6844         };
6845     };
6846
6847     var createSingle = function(h, e, fn, scope){
6848         return function(){
6849             e.removeListener(fn, scope);
6850             return h.apply(scope, arguments);
6851         };
6852     };
6853
6854     var createDelayed = function(h, o, scope){
6855         return function(){
6856             var args = Array.prototype.slice.call(arguments, 0);
6857             setTimeout(function(){
6858                 h.apply(scope, args);
6859             }, o.delay || 10);
6860         };
6861     };
6862
6863     Roo.util.Event = function(obj, name){
6864         this.name = name;
6865         this.obj = obj;
6866         this.listeners = [];
6867     };
6868
6869     Roo.util.Event.prototype = {
6870         addListener : function(fn, scope, options){
6871             var o = options || {};
6872             scope = scope || this.obj;
6873             if(!this.isListening(fn, scope)){
6874                 var l = {fn: fn, scope: scope, options: o};
6875                 var h = fn;
6876                 if(o.delay){
6877                     h = createDelayed(h, o, scope);
6878                 }
6879                 if(o.single){
6880                     h = createSingle(h, this, fn, scope);
6881                 }
6882                 if(o.buffer){
6883                     h = createBuffered(h, o, scope);
6884                 }
6885                 l.fireFn = h;
6886                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6887                     this.listeners.push(l);
6888                 }else{
6889                     this.listeners = this.listeners.slice(0);
6890                     this.listeners.push(l);
6891                 }
6892             }
6893         },
6894
6895         findListener : function(fn, scope){
6896             scope = scope || this.obj;
6897             var ls = this.listeners;
6898             for(var i = 0, len = ls.length; i < len; i++){
6899                 var l = ls[i];
6900                 if(l.fn == fn && l.scope == scope){
6901                     return i;
6902                 }
6903             }
6904             return -1;
6905         },
6906
6907         isListening : function(fn, scope){
6908             return this.findListener(fn, scope) != -1;
6909         },
6910
6911         removeListener : function(fn, scope){
6912             var index;
6913             if((index = this.findListener(fn, scope)) != -1){
6914                 if(!this.firing){
6915                     this.listeners.splice(index, 1);
6916                 }else{
6917                     this.listeners = this.listeners.slice(0);
6918                     this.listeners.splice(index, 1);
6919                 }
6920                 return true;
6921             }
6922             return false;
6923         },
6924
6925         clearListeners : function(){
6926             this.listeners = [];
6927         },
6928
6929         fire : function(){
6930             var ls = this.listeners, scope, len = ls.length;
6931             if(len > 0){
6932                 this.firing = true;
6933                 var args = Array.prototype.slice.call(arguments, 0);                
6934                 for(var i = 0; i < len; i++){
6935                     var l = ls[i];
6936                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6937                         this.firing = false;
6938                         return false;
6939                     }
6940                 }
6941                 this.firing = false;
6942             }
6943             return true;
6944         }
6945     };
6946 })();/*
6947  * RooJS Library 
6948  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6949  *
6950  * Licence LGPL 
6951  *
6952  */
6953  
6954 /**
6955  * @class Roo.Document
6956  * @extends Roo.util.Observable
6957  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6958  * 
6959  * @param {Object} config the methods and properties of the 'base' class for the application.
6960  * 
6961  *  Generic Page handler - implement this to start your app..
6962  * 
6963  * eg.
6964  *  MyProject = new Roo.Document({
6965         events : {
6966             'load' : true // your events..
6967         },
6968         listeners : {
6969             'ready' : function() {
6970                 // fired on Roo.onReady()
6971             }
6972         }
6973  * 
6974  */
6975 Roo.Document = function(cfg) {
6976      
6977     this.addEvents({ 
6978         'ready' : true
6979     });
6980     Roo.util.Observable.call(this,cfg);
6981     
6982     var _this = this;
6983     
6984     Roo.onReady(function() {
6985         _this.fireEvent('ready');
6986     },null,false);
6987     
6988     
6989 }
6990
6991 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6992  * Based on:
6993  * Ext JS Library 1.1.1
6994  * Copyright(c) 2006-2007, Ext JS, LLC.
6995  *
6996  * Originally Released Under LGPL - original licence link has changed is not relivant.
6997  *
6998  * Fork - LGPL
6999  * <script type="text/javascript">
7000  */
7001
7002 /**
7003  * @class Roo.EventManager
7004  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7005  * several useful events directly.
7006  * See {@link Roo.EventObject} for more details on normalized event objects.
7007  * @static
7008  */
7009 Roo.EventManager = function(){
7010     var docReadyEvent, docReadyProcId, docReadyState = false;
7011     var resizeEvent, resizeTask, textEvent, textSize;
7012     var E = Roo.lib.Event;
7013     var D = Roo.lib.Dom;
7014
7015     
7016     
7017
7018     var fireDocReady = function(){
7019         if(!docReadyState){
7020             docReadyState = true;
7021             Roo.isReady = true;
7022             if(docReadyProcId){
7023                 clearInterval(docReadyProcId);
7024             }
7025             if(Roo.isGecko || Roo.isOpera) {
7026                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7027             }
7028             if(Roo.isIE){
7029                 var defer = document.getElementById("ie-deferred-loader");
7030                 if(defer){
7031                     defer.onreadystatechange = null;
7032                     defer.parentNode.removeChild(defer);
7033                 }
7034             }
7035             if(docReadyEvent){
7036                 docReadyEvent.fire();
7037                 docReadyEvent.clearListeners();
7038             }
7039         }
7040     };
7041     
7042     var initDocReady = function(){
7043         docReadyEvent = new Roo.util.Event();
7044         if(Roo.isGecko || Roo.isOpera) {
7045             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7046         }else if(Roo.isIE){
7047             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7048             var defer = document.getElementById("ie-deferred-loader");
7049             defer.onreadystatechange = function(){
7050                 if(this.readyState == "complete"){
7051                     fireDocReady();
7052                 }
7053             };
7054         }else if(Roo.isSafari){ 
7055             docReadyProcId = setInterval(function(){
7056                 var rs = document.readyState;
7057                 if(rs == "complete") {
7058                     fireDocReady();     
7059                  }
7060             }, 10);
7061         }
7062         // no matter what, make sure it fires on load
7063         E.on(window, "load", fireDocReady);
7064     };
7065
7066     var createBuffered = function(h, o){
7067         var task = new Roo.util.DelayedTask(h);
7068         return function(e){
7069             // create new event object impl so new events don't wipe out properties
7070             e = new Roo.EventObjectImpl(e);
7071             task.delay(o.buffer, h, null, [e]);
7072         };
7073     };
7074
7075     var createSingle = function(h, el, ename, fn){
7076         return function(e){
7077             Roo.EventManager.removeListener(el, ename, fn);
7078             h(e);
7079         };
7080     };
7081
7082     var createDelayed = function(h, o){
7083         return function(e){
7084             // create new event object impl so new events don't wipe out properties
7085             e = new Roo.EventObjectImpl(e);
7086             setTimeout(function(){
7087                 h(e);
7088             }, o.delay || 10);
7089         };
7090     };
7091     var transitionEndVal = false;
7092     
7093     var transitionEnd = function()
7094     {
7095         if (transitionEndVal) {
7096             return transitionEndVal;
7097         }
7098         var el = document.createElement('div');
7099
7100         var transEndEventNames = {
7101             WebkitTransition : 'webkitTransitionEnd',
7102             MozTransition    : 'transitionend',
7103             OTransition      : 'oTransitionEnd otransitionend',
7104             transition       : 'transitionend'
7105         };
7106     
7107         for (var name in transEndEventNames) {
7108             if (el.style[name] !== undefined) {
7109                 transitionEndVal = transEndEventNames[name];
7110                 return  transitionEndVal ;
7111             }
7112         }
7113     }
7114     
7115   
7116
7117     var listen = function(element, ename, opt, fn, scope)
7118     {
7119         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7120         fn = fn || o.fn; scope = scope || o.scope;
7121         var el = Roo.getDom(element);
7122         
7123         
7124         if(!el){
7125             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7126         }
7127         
7128         if (ename == 'transitionend') {
7129             ename = transitionEnd();
7130         }
7131         var h = function(e){
7132             e = Roo.EventObject.setEvent(e);
7133             var t;
7134             if(o.delegate){
7135                 t = e.getTarget(o.delegate, el);
7136                 if(!t){
7137                     return;
7138                 }
7139             }else{
7140                 t = e.target;
7141             }
7142             if(o.stopEvent === true){
7143                 e.stopEvent();
7144             }
7145             if(o.preventDefault === true){
7146                e.preventDefault();
7147             }
7148             if(o.stopPropagation === true){
7149                 e.stopPropagation();
7150             }
7151
7152             if(o.normalized === false){
7153                 e = e.browserEvent;
7154             }
7155
7156             fn.call(scope || el, e, t, o);
7157         };
7158         if(o.delay){
7159             h = createDelayed(h, o);
7160         }
7161         if(o.single){
7162             h = createSingle(h, el, ename, fn);
7163         }
7164         if(o.buffer){
7165             h = createBuffered(h, o);
7166         }
7167         
7168         fn._handlers = fn._handlers || [];
7169         
7170         
7171         fn._handlers.push([Roo.id(el), ename, h]);
7172         
7173         
7174          
7175         E.on(el, ename, h); // this adds the actuall listener to the object..
7176         
7177         
7178         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7179             el.addEventListener("DOMMouseScroll", h, false);
7180             E.on(window, 'unload', function(){
7181                 el.removeEventListener("DOMMouseScroll", h, false);
7182             });
7183         }
7184         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7185             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7186         }
7187         return h;
7188     };
7189
7190     var stopListening = function(el, ename, fn){
7191         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7192         if(hds){
7193             for(var i = 0, len = hds.length; i < len; i++){
7194                 var h = hds[i];
7195                 if(h[0] == id && h[1] == ename){
7196                     hd = h[2];
7197                     hds.splice(i, 1);
7198                     break;
7199                 }
7200             }
7201         }
7202         E.un(el, ename, hd);
7203         el = Roo.getDom(el);
7204         if(ename == "mousewheel" && el.addEventListener){
7205             el.removeEventListener("DOMMouseScroll", hd, false);
7206         }
7207         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7208             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7209         }
7210     };
7211
7212     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7213     
7214     var pub = {
7215         
7216         
7217         /** 
7218          * Fix for doc tools
7219          * @scope Roo.EventManager
7220          */
7221         
7222         
7223         /** 
7224          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7225          * object with a Roo.EventObject
7226          * @param {Function} fn        The method the event invokes
7227          * @param {Object}   scope    An object that becomes the scope of the handler
7228          * @param {boolean}  override If true, the obj passed in becomes
7229          *                             the execution scope of the listener
7230          * @return {Function} The wrapped function
7231          * @deprecated
7232          */
7233         wrap : function(fn, scope, override){
7234             return function(e){
7235                 Roo.EventObject.setEvent(e);
7236                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7237             };
7238         },
7239         
7240         /**
7241      * Appends an event handler to an element (shorthand for addListener)
7242      * @param {String/HTMLElement}   element        The html element or id to assign the
7243      * @param {String}   eventName The type of event to listen for
7244      * @param {Function} handler The method the event invokes
7245      * @param {Object}   scope (optional) The scope in which to execute the handler
7246      * function. The handler function's "this" context.
7247      * @param {Object}   options (optional) An object containing handler configuration
7248      * properties. This may contain any of the following properties:<ul>
7249      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7250      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7251      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7252      * <li>preventDefault {Boolean} True to prevent the default action</li>
7253      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7254      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7255      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7256      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7257      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7258      * by the specified number of milliseconds. If the event fires again within that time, the original
7259      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7260      * </ul><br>
7261      * <p>
7262      * <b>Combining Options</b><br>
7263      * Using the options argument, it is possible to combine different types of listeners:<br>
7264      * <br>
7265      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7266      * Code:<pre><code>
7267 el.on('click', this.onClick, this, {
7268     single: true,
7269     delay: 100,
7270     stopEvent : true,
7271     forumId: 4
7272 });</code></pre>
7273      * <p>
7274      * <b>Attaching multiple handlers in 1 call</b><br>
7275       * The method also allows for a single argument to be passed which is a config object containing properties
7276      * which specify multiple handlers.
7277      * <p>
7278      * Code:<pre><code>
7279 el.on({
7280     'click' : {
7281         fn: this.onClick
7282         scope: this,
7283         delay: 100
7284     },
7285     'mouseover' : {
7286         fn: this.onMouseOver
7287         scope: this
7288     },
7289     'mouseout' : {
7290         fn: this.onMouseOut
7291         scope: this
7292     }
7293 });</code></pre>
7294      * <p>
7295      * Or a shorthand syntax:<br>
7296      * Code:<pre><code>
7297 el.on({
7298     'click' : this.onClick,
7299     'mouseover' : this.onMouseOver,
7300     'mouseout' : this.onMouseOut
7301     scope: this
7302 });</code></pre>
7303      */
7304         addListener : function(element, eventName, fn, scope, options){
7305             if(typeof eventName == "object"){
7306                 var o = eventName;
7307                 for(var e in o){
7308                     if(propRe.test(e)){
7309                         continue;
7310                     }
7311                     if(typeof o[e] == "function"){
7312                         // shared options
7313                         listen(element, e, o, o[e], o.scope);
7314                     }else{
7315                         // individual options
7316                         listen(element, e, o[e]);
7317                     }
7318                 }
7319                 return;
7320             }
7321             return listen(element, eventName, options, fn, scope);
7322         },
7323         
7324         /**
7325          * Removes an event handler
7326          *
7327          * @param {String/HTMLElement}   element        The id or html element to remove the 
7328          *                             event from
7329          * @param {String}   eventName     The type of event
7330          * @param {Function} fn
7331          * @return {Boolean} True if a listener was actually removed
7332          */
7333         removeListener : function(element, eventName, fn){
7334             return stopListening(element, eventName, fn);
7335         },
7336         
7337         /**
7338          * Fires when the document is ready (before onload and before images are loaded). Can be 
7339          * accessed shorthanded Roo.onReady().
7340          * @param {Function} fn        The method the event invokes
7341          * @param {Object}   scope    An  object that becomes the scope of the handler
7342          * @param {boolean}  options
7343          */
7344         onDocumentReady : function(fn, scope, options){
7345             if(docReadyState){ // if it already fired
7346                 docReadyEvent.addListener(fn, scope, options);
7347                 docReadyEvent.fire();
7348                 docReadyEvent.clearListeners();
7349                 return;
7350             }
7351             if(!docReadyEvent){
7352                 initDocReady();
7353             }
7354             docReadyEvent.addListener(fn, scope, options);
7355         },
7356         
7357         /**
7358          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7359          * @param {Function} fn        The method the event invokes
7360          * @param {Object}   scope    An object that becomes the scope of the handler
7361          * @param {boolean}  options
7362          */
7363         onWindowResize : function(fn, scope, options)
7364         {
7365             if(!resizeEvent){
7366                 resizeEvent = new Roo.util.Event();
7367                 resizeTask = new Roo.util.DelayedTask(function(){
7368                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7369                 });
7370                 E.on(window, "resize", function()
7371                 {
7372                     if (Roo.isIE) {
7373                         resizeTask.delay(50);
7374                     } else {
7375                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7376                     }
7377                 });
7378             }
7379             resizeEvent.addListener(fn, scope, options);
7380         },
7381
7382         /**
7383          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7384          * @param {Function} fn        The method the event invokes
7385          * @param {Object}   scope    An object that becomes the scope of the handler
7386          * @param {boolean}  options
7387          */
7388         onTextResize : function(fn, scope, options){
7389             if(!textEvent){
7390                 textEvent = new Roo.util.Event();
7391                 var textEl = new Roo.Element(document.createElement('div'));
7392                 textEl.dom.className = 'x-text-resize';
7393                 textEl.dom.innerHTML = 'X';
7394                 textEl.appendTo(document.body);
7395                 textSize = textEl.dom.offsetHeight;
7396                 setInterval(function(){
7397                     if(textEl.dom.offsetHeight != textSize){
7398                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7399                     }
7400                 }, this.textResizeInterval);
7401             }
7402             textEvent.addListener(fn, scope, options);
7403         },
7404
7405         /**
7406          * Removes the passed window resize listener.
7407          * @param {Function} fn        The method the event invokes
7408          * @param {Object}   scope    The scope of handler
7409          */
7410         removeResizeListener : function(fn, scope){
7411             if(resizeEvent){
7412                 resizeEvent.removeListener(fn, scope);
7413             }
7414         },
7415
7416         // private
7417         fireResize : function(){
7418             if(resizeEvent){
7419                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7420             }   
7421         },
7422         /**
7423          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7424          */
7425         ieDeferSrc : false,
7426         /**
7427          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7428          */
7429         textResizeInterval : 50
7430     };
7431     
7432     /**
7433      * Fix for doc tools
7434      * @scopeAlias pub=Roo.EventManager
7435      */
7436     
7437      /**
7438      * Appends an event handler to an element (shorthand for addListener)
7439      * @param {String/HTMLElement}   element        The html element or id to assign the
7440      * @param {String}   eventName The type of event to listen for
7441      * @param {Function} handler The method the event invokes
7442      * @param {Object}   scope (optional) The scope in which to execute the handler
7443      * function. The handler function's "this" context.
7444      * @param {Object}   options (optional) An object containing handler configuration
7445      * properties. This may contain any of the following properties:<ul>
7446      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7447      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7448      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7449      * <li>preventDefault {Boolean} True to prevent the default action</li>
7450      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7451      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7452      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7453      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7454      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7455      * by the specified number of milliseconds. If the event fires again within that time, the original
7456      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7457      * </ul><br>
7458      * <p>
7459      * <b>Combining Options</b><br>
7460      * Using the options argument, it is possible to combine different types of listeners:<br>
7461      * <br>
7462      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7463      * Code:<pre><code>
7464 el.on('click', this.onClick, this, {
7465     single: true,
7466     delay: 100,
7467     stopEvent : true,
7468     forumId: 4
7469 });</code></pre>
7470      * <p>
7471      * <b>Attaching multiple handlers in 1 call</b><br>
7472       * The method also allows for a single argument to be passed which is a config object containing properties
7473      * which specify multiple handlers.
7474      * <p>
7475      * Code:<pre><code>
7476 el.on({
7477     'click' : {
7478         fn: this.onClick
7479         scope: this,
7480         delay: 100
7481     },
7482     'mouseover' : {
7483         fn: this.onMouseOver
7484         scope: this
7485     },
7486     'mouseout' : {
7487         fn: this.onMouseOut
7488         scope: this
7489     }
7490 });</code></pre>
7491      * <p>
7492      * Or a shorthand syntax:<br>
7493      * Code:<pre><code>
7494 el.on({
7495     'click' : this.onClick,
7496     'mouseover' : this.onMouseOver,
7497     'mouseout' : this.onMouseOut
7498     scope: this
7499 });</code></pre>
7500      */
7501     pub.on = pub.addListener;
7502     pub.un = pub.removeListener;
7503
7504     pub.stoppedMouseDownEvent = new Roo.util.Event();
7505     return pub;
7506 }();
7507 /**
7508   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7509   * @param {Function} fn        The method the event invokes
7510   * @param {Object}   scope    An  object that becomes the scope of the handler
7511   * @param {boolean}  override If true, the obj passed in becomes
7512   *                             the execution scope of the listener
7513   * @member Roo
7514   * @method onReady
7515  */
7516 Roo.onReady = Roo.EventManager.onDocumentReady;
7517
7518 Roo.onReady(function(){
7519     var bd = Roo.get(document.body);
7520     if(!bd){ return; }
7521
7522     var cls = [
7523             Roo.isIE ? "roo-ie"
7524             : Roo.isIE11 ? "roo-ie11"
7525             : Roo.isEdge ? "roo-edge"
7526             : Roo.isGecko ? "roo-gecko"
7527             : Roo.isOpera ? "roo-opera"
7528             : Roo.isSafari ? "roo-safari" : ""];
7529
7530     if(Roo.isMac){
7531         cls.push("roo-mac");
7532     }
7533     if(Roo.isLinux){
7534         cls.push("roo-linux");
7535     }
7536     if(Roo.isIOS){
7537         cls.push("roo-ios");
7538     }
7539     if(Roo.isTouch){
7540         cls.push("roo-touch");
7541     }
7542     if(Roo.isBorderBox){
7543         cls.push('roo-border-box');
7544     }
7545     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7546         var p = bd.dom.parentNode;
7547         if(p){
7548             p.className += ' roo-strict';
7549         }
7550     }
7551     bd.addClass(cls.join(' '));
7552 });
7553
7554 /**
7555  * @class Roo.EventObject
7556  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7557  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7558  * Example:
7559  * <pre><code>
7560  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7561     e.preventDefault();
7562     var target = e.getTarget();
7563     ...
7564  }
7565  var myDiv = Roo.get("myDiv");
7566  myDiv.on("click", handleClick);
7567  //or
7568  Roo.EventManager.on("myDiv", 'click', handleClick);
7569  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7570  </code></pre>
7571  * @static
7572  */
7573 Roo.EventObject = function(){
7574     
7575     var E = Roo.lib.Event;
7576     
7577     // safari keypress events for special keys return bad keycodes
7578     var safariKeys = {
7579         63234 : 37, // left
7580         63235 : 39, // right
7581         63232 : 38, // up
7582         63233 : 40, // down
7583         63276 : 33, // page up
7584         63277 : 34, // page down
7585         63272 : 46, // delete
7586         63273 : 36, // home
7587         63275 : 35  // end
7588     };
7589
7590     // normalize button clicks
7591     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7592                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7593
7594     Roo.EventObjectImpl = function(e){
7595         if(e){
7596             this.setEvent(e.browserEvent || e);
7597         }
7598     };
7599     Roo.EventObjectImpl.prototype = {
7600         /**
7601          * Used to fix doc tools.
7602          * @scope Roo.EventObject.prototype
7603          */
7604             
7605
7606         
7607         
7608         /** The normal browser event */
7609         browserEvent : null,
7610         /** The button pressed in a mouse event */
7611         button : -1,
7612         /** True if the shift key was down during the event */
7613         shiftKey : false,
7614         /** True if the control key was down during the event */
7615         ctrlKey : false,
7616         /** True if the alt key was down during the event */
7617         altKey : false,
7618
7619         /** Key constant 
7620         * @type Number */
7621         BACKSPACE : 8,
7622         /** Key constant 
7623         * @type Number */
7624         TAB : 9,
7625         /** Key constant 
7626         * @type Number */
7627         RETURN : 13,
7628         /** Key constant 
7629         * @type Number */
7630         ENTER : 13,
7631         /** Key constant 
7632         * @type Number */
7633         SHIFT : 16,
7634         /** Key constant 
7635         * @type Number */
7636         CONTROL : 17,
7637         /** Key constant 
7638         * @type Number */
7639         ESC : 27,
7640         /** Key constant 
7641         * @type Number */
7642         SPACE : 32,
7643         /** Key constant 
7644         * @type Number */
7645         PAGEUP : 33,
7646         /** Key constant 
7647         * @type Number */
7648         PAGEDOWN : 34,
7649         /** Key constant 
7650         * @type Number */
7651         END : 35,
7652         /** Key constant 
7653         * @type Number */
7654         HOME : 36,
7655         /** Key constant 
7656         * @type Number */
7657         LEFT : 37,
7658         /** Key constant 
7659         * @type Number */
7660         UP : 38,
7661         /** Key constant 
7662         * @type Number */
7663         RIGHT : 39,
7664         /** Key constant 
7665         * @type Number */
7666         DOWN : 40,
7667         /** Key constant 
7668         * @type Number */
7669         DELETE : 46,
7670         /** Key constant 
7671         * @type Number */
7672         F5 : 116,
7673
7674            /** @private */
7675         setEvent : function(e){
7676             if(e == this || (e && e.browserEvent)){ // already wrapped
7677                 return e;
7678             }
7679             this.browserEvent = e;
7680             if(e){
7681                 // normalize buttons
7682                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7683                 if(e.type == 'click' && this.button == -1){
7684                     this.button = 0;
7685                 }
7686                 this.type = e.type;
7687                 this.shiftKey = e.shiftKey;
7688                 // mac metaKey behaves like ctrlKey
7689                 this.ctrlKey = e.ctrlKey || e.metaKey;
7690                 this.altKey = e.altKey;
7691                 // in getKey these will be normalized for the mac
7692                 this.keyCode = e.keyCode;
7693                 // keyup warnings on firefox.
7694                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7695                 // cache the target for the delayed and or buffered events
7696                 this.target = E.getTarget(e);
7697                 // same for XY
7698                 this.xy = E.getXY(e);
7699             }else{
7700                 this.button = -1;
7701                 this.shiftKey = false;
7702                 this.ctrlKey = false;
7703                 this.altKey = false;
7704                 this.keyCode = 0;
7705                 this.charCode =0;
7706                 this.target = null;
7707                 this.xy = [0, 0];
7708             }
7709             return this;
7710         },
7711
7712         /**
7713          * Stop the event (preventDefault and stopPropagation)
7714          */
7715         stopEvent : function(){
7716             if(this.browserEvent){
7717                 if(this.browserEvent.type == 'mousedown'){
7718                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7719                 }
7720                 E.stopEvent(this.browserEvent);
7721             }
7722         },
7723
7724         /**
7725          * Prevents the browsers default handling of the event.
7726          */
7727         preventDefault : function(){
7728             if(this.browserEvent){
7729                 E.preventDefault(this.browserEvent);
7730             }
7731         },
7732
7733         /** @private */
7734         isNavKeyPress : function(){
7735             var k = this.keyCode;
7736             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7737             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7738         },
7739
7740         isSpecialKey : function(){
7741             var k = this.keyCode;
7742             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7743             (k == 16) || (k == 17) ||
7744             (k >= 18 && k <= 20) ||
7745             (k >= 33 && k <= 35) ||
7746             (k >= 36 && k <= 39) ||
7747             (k >= 44 && k <= 45);
7748         },
7749         /**
7750          * Cancels bubbling of the event.
7751          */
7752         stopPropagation : function(){
7753             if(this.browserEvent){
7754                 if(this.type == 'mousedown'){
7755                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7756                 }
7757                 E.stopPropagation(this.browserEvent);
7758             }
7759         },
7760
7761         /**
7762          * Gets the key code for the event.
7763          * @return {Number}
7764          */
7765         getCharCode : function(){
7766             return this.charCode || this.keyCode;
7767         },
7768
7769         /**
7770          * Returns a normalized keyCode for the event.
7771          * @return {Number} The key code
7772          */
7773         getKey : function(){
7774             var k = this.keyCode || this.charCode;
7775             return Roo.isSafari ? (safariKeys[k] || k) : k;
7776         },
7777
7778         /**
7779          * Gets the x coordinate of the event.
7780          * @return {Number}
7781          */
7782         getPageX : function(){
7783             return this.xy[0];
7784         },
7785
7786         /**
7787          * Gets the y coordinate of the event.
7788          * @return {Number}
7789          */
7790         getPageY : function(){
7791             return this.xy[1];
7792         },
7793
7794         /**
7795          * Gets the time of the event.
7796          * @return {Number}
7797          */
7798         getTime : function(){
7799             if(this.browserEvent){
7800                 return E.getTime(this.browserEvent);
7801             }
7802             return null;
7803         },
7804
7805         /**
7806          * Gets the page coordinates of the event.
7807          * @return {Array} The xy values like [x, y]
7808          */
7809         getXY : function(){
7810             return this.xy;
7811         },
7812
7813         /**
7814          * Gets the target for the event.
7815          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7816          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7817                 search as a number or element (defaults to 10 || document.body)
7818          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7819          * @return {HTMLelement}
7820          */
7821         getTarget : function(selector, maxDepth, returnEl){
7822             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7823         },
7824         /**
7825          * Gets the related target.
7826          * @return {HTMLElement}
7827          */
7828         getRelatedTarget : function(){
7829             if(this.browserEvent){
7830                 return E.getRelatedTarget(this.browserEvent);
7831             }
7832             return null;
7833         },
7834
7835         /**
7836          * Normalizes mouse wheel delta across browsers
7837          * @return {Number} The delta
7838          */
7839         getWheelDelta : function(){
7840             var e = this.browserEvent;
7841             var delta = 0;
7842             if(e.wheelDelta){ /* IE/Opera. */
7843                 delta = e.wheelDelta/120;
7844             }else if(e.detail){ /* Mozilla case. */
7845                 delta = -e.detail/3;
7846             }
7847             return delta;
7848         },
7849
7850         /**
7851          * Returns true if the control, meta, shift or alt key was pressed during this event.
7852          * @return {Boolean}
7853          */
7854         hasModifier : function(){
7855             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7856         },
7857
7858         /**
7859          * Returns true if the target of this event equals el or is a child of el
7860          * @param {String/HTMLElement/Element} el
7861          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7862          * @return {Boolean}
7863          */
7864         within : function(el, related){
7865             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7866             return t && Roo.fly(el).contains(t);
7867         },
7868
7869         getPoint : function(){
7870             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7871         }
7872     };
7873
7874     return new Roo.EventObjectImpl();
7875 }();
7876             
7877     /*
7878  * Based on:
7879  * Ext JS Library 1.1.1
7880  * Copyright(c) 2006-2007, Ext JS, LLC.
7881  *
7882  * Originally Released Under LGPL - original licence link has changed is not relivant.
7883  *
7884  * Fork - LGPL
7885  * <script type="text/javascript">
7886  */
7887
7888  
7889 // was in Composite Element!??!?!
7890  
7891 (function(){
7892     var D = Roo.lib.Dom;
7893     var E = Roo.lib.Event;
7894     var A = Roo.lib.Anim;
7895
7896     // local style camelizing for speed
7897     var propCache = {};
7898     var camelRe = /(-[a-z])/gi;
7899     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7900     var view = document.defaultView;
7901
7902 /**
7903  * @class Roo.Element
7904  * Represents an Element in the DOM.<br><br>
7905  * Usage:<br>
7906 <pre><code>
7907 var el = Roo.get("my-div");
7908
7909 // or with getEl
7910 var el = getEl("my-div");
7911
7912 // or with a DOM element
7913 var el = Roo.get(myDivElement);
7914 </code></pre>
7915  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7916  * each call instead of constructing a new one.<br><br>
7917  * <b>Animations</b><br />
7918  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7919  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7920 <pre>
7921 Option    Default   Description
7922 --------- --------  ---------------------------------------------
7923 duration  .35       The duration of the animation in seconds
7924 easing    easeOut   The YUI easing method
7925 callback  none      A function to execute when the anim completes
7926 scope     this      The scope (this) of the callback function
7927 </pre>
7928 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7929 * manipulate the animation. Here's an example:
7930 <pre><code>
7931 var el = Roo.get("my-div");
7932
7933 // no animation
7934 el.setWidth(100);
7935
7936 // default animation
7937 el.setWidth(100, true);
7938
7939 // animation with some options set
7940 el.setWidth(100, {
7941     duration: 1,
7942     callback: this.foo,
7943     scope: this
7944 });
7945
7946 // using the "anim" property to get the Anim object
7947 var opt = {
7948     duration: 1,
7949     callback: this.foo,
7950     scope: this
7951 };
7952 el.setWidth(100, opt);
7953 ...
7954 if(opt.anim.isAnimated()){
7955     opt.anim.stop();
7956 }
7957 </code></pre>
7958 * <b> Composite (Collections of) Elements</b><br />
7959  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7960  * @constructor Create a new Element directly.
7961  * @param {String/HTMLElement} element
7962  * @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).
7963  */
7964     Roo.Element = function(element, forceNew)
7965     {
7966         var dom = typeof element == "string" ?
7967                 document.getElementById(element) : element;
7968         
7969         this.listeners = {};
7970         
7971         if(!dom){ // invalid id/element
7972             return null;
7973         }
7974         var id = dom.id;
7975         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7976             return Roo.Element.cache[id];
7977         }
7978
7979         /**
7980          * The DOM element
7981          * @type HTMLElement
7982          */
7983         this.dom = dom;
7984
7985         /**
7986          * The DOM element ID
7987          * @type String
7988          */
7989         this.id = id || Roo.id(dom);
7990         
7991         return this; // assumed for cctor?
7992     };
7993
7994     var El = Roo.Element;
7995
7996     El.prototype = {
7997         /**
7998          * The element's default display mode  (defaults to "") 
7999          * @type String
8000          */
8001         originalDisplay : "",
8002
8003         
8004         // note this is overridden in BS version..
8005         visibilityMode : 1, 
8006         /**
8007          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8008          * @type String
8009          */
8010         defaultUnit : "px",
8011         
8012         /**
8013          * Sets the element's visibility mode. When setVisible() is called it
8014          * will use this to determine whether to set the visibility or the display property.
8015          * @param visMode Element.VISIBILITY or Element.DISPLAY
8016          * @return {Roo.Element} this
8017          */
8018         setVisibilityMode : function(visMode){
8019             this.visibilityMode = visMode;
8020             return this;
8021         },
8022         /**
8023          * Convenience method for setVisibilityMode(Element.DISPLAY)
8024          * @param {String} display (optional) What to set display to when visible
8025          * @return {Roo.Element} this
8026          */
8027         enableDisplayMode : function(display){
8028             this.setVisibilityMode(El.DISPLAY);
8029             if(typeof display != "undefined") { this.originalDisplay = display; }
8030             return this;
8031         },
8032
8033         /**
8034          * 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)
8035          * @param {String} selector The simple selector to test
8036          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8037                 search as a number or element (defaults to 10 || document.body)
8038          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8039          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8040          */
8041         findParent : function(simpleSelector, maxDepth, returnEl){
8042             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8043             maxDepth = maxDepth || 50;
8044             if(typeof maxDepth != "number"){
8045                 stopEl = Roo.getDom(maxDepth);
8046                 maxDepth = 10;
8047             }
8048             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8049                 if(dq.is(p, simpleSelector)){
8050                     return returnEl ? Roo.get(p) : p;
8051                 }
8052                 depth++;
8053                 p = p.parentNode;
8054             }
8055             return null;
8056         },
8057
8058
8059         /**
8060          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8061          * @param {String} selector The simple selector to test
8062          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8063                 search as a number or element (defaults to 10 || document.body)
8064          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8065          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8066          */
8067         findParentNode : function(simpleSelector, maxDepth, returnEl){
8068             var p = Roo.fly(this.dom.parentNode, '_internal');
8069             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8070         },
8071         
8072         /**
8073          * Looks at  the scrollable parent element
8074          */
8075         findScrollableParent : function()
8076         {
8077             var overflowRegex = /(auto|scroll)/;
8078             
8079             if(this.getStyle('position') === 'fixed'){
8080                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8081             }
8082             
8083             var excludeStaticParent = this.getStyle('position') === "absolute";
8084             
8085             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8086                 
8087                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8088                     continue;
8089                 }
8090                 
8091                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8092                     return parent;
8093                 }
8094                 
8095                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8096                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8097                 }
8098             }
8099             
8100             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8101         },
8102
8103         /**
8104          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8105          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8106          * @param {String} selector The simple selector to test
8107          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8108                 search as a number or element (defaults to 10 || document.body)
8109          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8110          */
8111         up : function(simpleSelector, maxDepth){
8112             return this.findParentNode(simpleSelector, maxDepth, true);
8113         },
8114
8115
8116
8117         /**
8118          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8119          * @param {String} selector The simple selector to test
8120          * @return {Boolean} True if this element matches the selector, else false
8121          */
8122         is : function(simpleSelector){
8123             return Roo.DomQuery.is(this.dom, simpleSelector);
8124         },
8125
8126         /**
8127          * Perform animation on this element.
8128          * @param {Object} args The YUI animation control args
8129          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8130          * @param {Function} onComplete (optional) Function to call when animation completes
8131          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8132          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8133          * @return {Roo.Element} this
8134          */
8135         animate : function(args, duration, onComplete, easing, animType){
8136             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8137             return this;
8138         },
8139
8140         /*
8141          * @private Internal animation call
8142          */
8143         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8144             animType = animType || 'run';
8145             opt = opt || {};
8146             var anim = Roo.lib.Anim[animType](
8147                 this.dom, args,
8148                 (opt.duration || defaultDur) || .35,
8149                 (opt.easing || defaultEase) || 'easeOut',
8150                 function(){
8151                     Roo.callback(cb, this);
8152                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8153                 },
8154                 this
8155             );
8156             opt.anim = anim;
8157             return anim;
8158         },
8159
8160         // private legacy anim prep
8161         preanim : function(a, i){
8162             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8163         },
8164
8165         /**
8166          * Removes worthless text nodes
8167          * @param {Boolean} forceReclean (optional) By default the element
8168          * keeps track if it has been cleaned already so
8169          * you can call this over and over. However, if you update the element and
8170          * need to force a reclean, you can pass true.
8171          */
8172         clean : function(forceReclean){
8173             if(this.isCleaned && forceReclean !== true){
8174                 return this;
8175             }
8176             var ns = /\S/;
8177             var d = this.dom, n = d.firstChild, ni = -1;
8178             while(n){
8179                 var nx = n.nextSibling;
8180                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8181                     d.removeChild(n);
8182                 }else{
8183                     n.nodeIndex = ++ni;
8184                 }
8185                 n = nx;
8186             }
8187             this.isCleaned = true;
8188             return this;
8189         },
8190
8191         // private
8192         calcOffsetsTo : function(el){
8193             el = Roo.get(el);
8194             var d = el.dom;
8195             var restorePos = false;
8196             if(el.getStyle('position') == 'static'){
8197                 el.position('relative');
8198                 restorePos = true;
8199             }
8200             var x = 0, y =0;
8201             var op = this.dom;
8202             while(op && op != d && op.tagName != 'HTML'){
8203                 x+= op.offsetLeft;
8204                 y+= op.offsetTop;
8205                 op = op.offsetParent;
8206             }
8207             if(restorePos){
8208                 el.position('static');
8209             }
8210             return [x, y];
8211         },
8212
8213         /**
8214          * Scrolls this element into view within the passed container.
8215          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8216          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8217          * @return {Roo.Element} this
8218          */
8219         scrollIntoView : function(container, hscroll){
8220             var c = Roo.getDom(container) || document.body;
8221             var el = this.dom;
8222
8223             var o = this.calcOffsetsTo(c),
8224                 l = o[0],
8225                 t = o[1],
8226                 b = t+el.offsetHeight,
8227                 r = l+el.offsetWidth;
8228
8229             var ch = c.clientHeight;
8230             var ct = parseInt(c.scrollTop, 10);
8231             var cl = parseInt(c.scrollLeft, 10);
8232             var cb = ct + ch;
8233             var cr = cl + c.clientWidth;
8234
8235             if(t < ct){
8236                 c.scrollTop = t;
8237             }else if(b > cb){
8238                 c.scrollTop = b-ch;
8239             }
8240
8241             if(hscroll !== false){
8242                 if(l < cl){
8243                     c.scrollLeft = l;
8244                 }else if(r > cr){
8245                     c.scrollLeft = r-c.clientWidth;
8246                 }
8247             }
8248             return this;
8249         },
8250
8251         // private
8252         scrollChildIntoView : function(child, hscroll){
8253             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8254         },
8255
8256         /**
8257          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8258          * the new height may not be available immediately.
8259          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8260          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8261          * @param {Function} onComplete (optional) Function to call when animation completes
8262          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8263          * @return {Roo.Element} this
8264          */
8265         autoHeight : function(animate, duration, onComplete, easing){
8266             var oldHeight = this.getHeight();
8267             this.clip();
8268             this.setHeight(1); // force clipping
8269             setTimeout(function(){
8270                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8271                 if(!animate){
8272                     this.setHeight(height);
8273                     this.unclip();
8274                     if(typeof onComplete == "function"){
8275                         onComplete();
8276                     }
8277                 }else{
8278                     this.setHeight(oldHeight); // restore original height
8279                     this.setHeight(height, animate, duration, function(){
8280                         this.unclip();
8281                         if(typeof onComplete == "function") { onComplete(); }
8282                     }.createDelegate(this), easing);
8283                 }
8284             }.createDelegate(this), 0);
8285             return this;
8286         },
8287
8288         /**
8289          * Returns true if this element is an ancestor of the passed element
8290          * @param {HTMLElement/String} el The element to check
8291          * @return {Boolean} True if this element is an ancestor of el, else false
8292          */
8293         contains : function(el){
8294             if(!el){return false;}
8295             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8296         },
8297
8298         /**
8299          * Checks whether the element is currently visible using both visibility and display properties.
8300          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8301          * @return {Boolean} True if the element is currently visible, else false
8302          */
8303         isVisible : function(deep) {
8304             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8305             if(deep !== true || !vis){
8306                 return vis;
8307             }
8308             var p = this.dom.parentNode;
8309             while(p && p.tagName.toLowerCase() != "body"){
8310                 if(!Roo.fly(p, '_isVisible').isVisible()){
8311                     return false;
8312                 }
8313                 p = p.parentNode;
8314             }
8315             return true;
8316         },
8317
8318         /**
8319          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8320          * @param {String} selector The CSS selector
8321          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8322          * @return {CompositeElement/CompositeElementLite} The composite element
8323          */
8324         select : function(selector, unique){
8325             return El.select(selector, unique, this.dom);
8326         },
8327
8328         /**
8329          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8330          * @param {String} selector The CSS selector
8331          * @return {Array} An array of the matched nodes
8332          */
8333         query : function(selector, unique){
8334             return Roo.DomQuery.select(selector, this.dom);
8335         },
8336
8337         /**
8338          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8339          * @param {String} selector The CSS selector
8340          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8341          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8342          */
8343         child : function(selector, returnDom){
8344             var n = Roo.DomQuery.selectNode(selector, this.dom);
8345             return returnDom ? n : Roo.get(n);
8346         },
8347
8348         /**
8349          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8350          * @param {String} selector The CSS selector
8351          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8352          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8353          */
8354         down : function(selector, returnDom){
8355             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8356             return returnDom ? n : Roo.get(n);
8357         },
8358
8359         /**
8360          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8361          * @param {String} group The group the DD object is member of
8362          * @param {Object} config The DD config object
8363          * @param {Object} overrides An object containing methods to override/implement on the DD object
8364          * @return {Roo.dd.DD} The DD object
8365          */
8366         initDD : function(group, config, overrides){
8367             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8368             return Roo.apply(dd, overrides);
8369         },
8370
8371         /**
8372          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8373          * @param {String} group The group the DDProxy object is member of
8374          * @param {Object} config The DDProxy config object
8375          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8376          * @return {Roo.dd.DDProxy} The DDProxy object
8377          */
8378         initDDProxy : function(group, config, overrides){
8379             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8380             return Roo.apply(dd, overrides);
8381         },
8382
8383         /**
8384          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8385          * @param {String} group The group the DDTarget object is member of
8386          * @param {Object} config The DDTarget config object
8387          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8388          * @return {Roo.dd.DDTarget} The DDTarget object
8389          */
8390         initDDTarget : function(group, config, overrides){
8391             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8392             return Roo.apply(dd, overrides);
8393         },
8394
8395         /**
8396          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8397          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8398          * @param {Boolean} visible Whether the element is visible
8399          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8400          * @return {Roo.Element} this
8401          */
8402          setVisible : function(visible, animate){
8403             if(!animate || !A){
8404                 if(this.visibilityMode == El.DISPLAY){
8405                     this.setDisplayed(visible);
8406                 }else{
8407                     this.fixDisplay();
8408                     this.dom.style.visibility = visible ? "visible" : "hidden";
8409                 }
8410             }else{
8411                 // closure for composites
8412                 var dom = this.dom;
8413                 var visMode = this.visibilityMode;
8414                 if(visible){
8415                     this.setOpacity(.01);
8416                     this.setVisible(true);
8417                 }
8418                 this.anim({opacity: { to: (visible?1:0) }},
8419                       this.preanim(arguments, 1),
8420                       null, .35, 'easeIn', function(){
8421                          if(!visible){
8422                              if(visMode == El.DISPLAY){
8423                                  dom.style.display = "none";
8424                              }else{
8425                                  dom.style.visibility = "hidden";
8426                              }
8427                              Roo.get(dom).setOpacity(1);
8428                          }
8429                      });
8430             }
8431             return this;
8432         },
8433
8434         /**
8435          * Returns true if display is not "none"
8436          * @return {Boolean}
8437          */
8438         isDisplayed : function() {
8439             return this.getStyle("display") != "none";
8440         },
8441
8442         /**
8443          * Toggles the element's visibility or display, depending on visibility mode.
8444          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8445          * @return {Roo.Element} this
8446          */
8447         toggle : function(animate){
8448             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8449             return this;
8450         },
8451
8452         /**
8453          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8454          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8455          * @return {Roo.Element} this
8456          */
8457         setDisplayed : function(value) {
8458             if(typeof value == "boolean"){
8459                value = value ? this.originalDisplay : "none";
8460             }
8461             this.setStyle("display", value);
8462             return this;
8463         },
8464
8465         /**
8466          * Tries to focus the element. Any exceptions are caught and ignored.
8467          * @return {Roo.Element} this
8468          */
8469         focus : function() {
8470             try{
8471                 this.dom.focus();
8472             }catch(e){}
8473             return this;
8474         },
8475
8476         /**
8477          * Tries to blur the element. Any exceptions are caught and ignored.
8478          * @return {Roo.Element} this
8479          */
8480         blur : function() {
8481             try{
8482                 this.dom.blur();
8483             }catch(e){}
8484             return this;
8485         },
8486
8487         /**
8488          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8489          * @param {String/Array} className The CSS class to add, or an array of classes
8490          * @return {Roo.Element} this
8491          */
8492         addClass : function(className){
8493             if(className instanceof Array){
8494                 for(var i = 0, len = className.length; i < len; i++) {
8495                     this.addClass(className[i]);
8496                 }
8497             }else{
8498                 if(className && !this.hasClass(className)){
8499                     if (this.dom instanceof SVGElement) {
8500                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8501                     } else {
8502                         this.dom.className = this.dom.className + " " + className;
8503                     }
8504                 }
8505             }
8506             return this;
8507         },
8508
8509         /**
8510          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8511          * @param {String/Array} className The CSS class to add, or an array of classes
8512          * @return {Roo.Element} this
8513          */
8514         radioClass : function(className){
8515             var siblings = this.dom.parentNode.childNodes;
8516             for(var i = 0; i < siblings.length; i++) {
8517                 var s = siblings[i];
8518                 if(s.nodeType == 1){
8519                     Roo.get(s).removeClass(className);
8520                 }
8521             }
8522             this.addClass(className);
8523             return this;
8524         },
8525
8526         /**
8527          * Removes one or more CSS classes from the element.
8528          * @param {String/Array} className The CSS class to remove, or an array of classes
8529          * @return {Roo.Element} this
8530          */
8531         removeClass : function(className){
8532             
8533             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8534             if(!className || !cn){
8535                 return this;
8536             }
8537             if(className instanceof Array){
8538                 for(var i = 0, len = className.length; i < len; i++) {
8539                     this.removeClass(className[i]);
8540                 }
8541             }else{
8542                 if(this.hasClass(className)){
8543                     var re = this.classReCache[className];
8544                     if (!re) {
8545                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8546                        this.classReCache[className] = re;
8547                     }
8548                     if (this.dom instanceof SVGElement) {
8549                         this.dom.className.baseVal = cn.replace(re, " ");
8550                     } else {
8551                         this.dom.className = cn.replace(re, " ");
8552                     }
8553                 }
8554             }
8555             return this;
8556         },
8557
8558         // private
8559         classReCache: {},
8560
8561         /**
8562          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8563          * @param {String} className The CSS class to toggle
8564          * @return {Roo.Element} this
8565          */
8566         toggleClass : function(className){
8567             if(this.hasClass(className)){
8568                 this.removeClass(className);
8569             }else{
8570                 this.addClass(className);
8571             }
8572             return this;
8573         },
8574
8575         /**
8576          * Checks if the specified CSS class exists on this element's DOM node.
8577          * @param {String} className The CSS class to check for
8578          * @return {Boolean} True if the class exists, else false
8579          */
8580         hasClass : function(className){
8581             if (this.dom instanceof SVGElement) {
8582                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8583             } 
8584             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8585         },
8586
8587         /**
8588          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8589          * @param {String} oldClassName The CSS class to replace
8590          * @param {String} newClassName The replacement CSS class
8591          * @return {Roo.Element} this
8592          */
8593         replaceClass : function(oldClassName, newClassName){
8594             this.removeClass(oldClassName);
8595             this.addClass(newClassName);
8596             return this;
8597         },
8598
8599         /**
8600          * Returns an object with properties matching the styles requested.
8601          * For example, el.getStyles('color', 'font-size', 'width') might return
8602          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8603          * @param {String} style1 A style name
8604          * @param {String} style2 A style name
8605          * @param {String} etc.
8606          * @return {Object} The style object
8607          */
8608         getStyles : function(){
8609             var a = arguments, len = a.length, r = {};
8610             for(var i = 0; i < len; i++){
8611                 r[a[i]] = this.getStyle(a[i]);
8612             }
8613             return r;
8614         },
8615
8616         /**
8617          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8618          * @param {String} property The style property whose value is returned.
8619          * @return {String} The current value of the style property for this element.
8620          */
8621         getStyle : function(){
8622             return view && view.getComputedStyle ?
8623                 function(prop){
8624                     var el = this.dom, v, cs, camel;
8625                     if(prop == 'float'){
8626                         prop = "cssFloat";
8627                     }
8628                     if(el.style && (v = el.style[prop])){
8629                         return v;
8630                     }
8631                     if(cs = view.getComputedStyle(el, "")){
8632                         if(!(camel = propCache[prop])){
8633                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8634                         }
8635                         return cs[camel];
8636                     }
8637                     return null;
8638                 } :
8639                 function(prop){
8640                     var el = this.dom, v, cs, camel;
8641                     if(prop == 'opacity'){
8642                         if(typeof el.style.filter == 'string'){
8643                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8644                             if(m){
8645                                 var fv = parseFloat(m[1]);
8646                                 if(!isNaN(fv)){
8647                                     return fv ? fv / 100 : 0;
8648                                 }
8649                             }
8650                         }
8651                         return 1;
8652                     }else if(prop == 'float'){
8653                         prop = "styleFloat";
8654                     }
8655                     if(!(camel = propCache[prop])){
8656                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8657                     }
8658                     if(v = el.style[camel]){
8659                         return v;
8660                     }
8661                     if(cs = el.currentStyle){
8662                         return cs[camel];
8663                     }
8664                     return null;
8665                 };
8666         }(),
8667
8668         /**
8669          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8670          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8671          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8672          * @return {Roo.Element} this
8673          */
8674         setStyle : function(prop, value){
8675             if(typeof prop == "string"){
8676                 
8677                 if (prop == 'float') {
8678                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8679                     return this;
8680                 }
8681                 
8682                 var camel;
8683                 if(!(camel = propCache[prop])){
8684                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8685                 }
8686                 
8687                 if(camel == 'opacity') {
8688                     this.setOpacity(value);
8689                 }else{
8690                     this.dom.style[camel] = value;
8691                 }
8692             }else{
8693                 for(var style in prop){
8694                     if(typeof prop[style] != "function"){
8695                        this.setStyle(style, prop[style]);
8696                     }
8697                 }
8698             }
8699             return this;
8700         },
8701
8702         /**
8703          * More flexible version of {@link #setStyle} for setting style properties.
8704          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8705          * a function which returns such a specification.
8706          * @return {Roo.Element} this
8707          */
8708         applyStyles : function(style){
8709             Roo.DomHelper.applyStyles(this.dom, style);
8710             return this;
8711         },
8712
8713         /**
8714           * 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).
8715           * @return {Number} The X position of the element
8716           */
8717         getX : function(){
8718             return D.getX(this.dom);
8719         },
8720
8721         /**
8722           * 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).
8723           * @return {Number} The Y position of the element
8724           */
8725         getY : function(){
8726             return D.getY(this.dom);
8727         },
8728
8729         /**
8730           * 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).
8731           * @return {Array} The XY position of the element
8732           */
8733         getXY : function(){
8734             return D.getXY(this.dom);
8735         },
8736
8737         /**
8738          * 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).
8739          * @param {Number} The X position of the element
8740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8741          * @return {Roo.Element} this
8742          */
8743         setX : function(x, animate){
8744             if(!animate || !A){
8745                 D.setX(this.dom, x);
8746             }else{
8747                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8748             }
8749             return this;
8750         },
8751
8752         /**
8753          * 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).
8754          * @param {Number} The Y position of the element
8755          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8756          * @return {Roo.Element} this
8757          */
8758         setY : function(y, animate){
8759             if(!animate || !A){
8760                 D.setY(this.dom, y);
8761             }else{
8762                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8763             }
8764             return this;
8765         },
8766
8767         /**
8768          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8769          * @param {String} left The left CSS property value
8770          * @return {Roo.Element} this
8771          */
8772         setLeft : function(left){
8773             this.setStyle("left", this.addUnits(left));
8774             return this;
8775         },
8776
8777         /**
8778          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8779          * @param {String} top The top CSS property value
8780          * @return {Roo.Element} this
8781          */
8782         setTop : function(top){
8783             this.setStyle("top", this.addUnits(top));
8784             return this;
8785         },
8786
8787         /**
8788          * Sets the element's CSS right style.
8789          * @param {String} right The right CSS property value
8790          * @return {Roo.Element} this
8791          */
8792         setRight : function(right){
8793             this.setStyle("right", this.addUnits(right));
8794             return this;
8795         },
8796
8797         /**
8798          * Sets the element's CSS bottom style.
8799          * @param {String} bottom The bottom CSS property value
8800          * @return {Roo.Element} this
8801          */
8802         setBottom : function(bottom){
8803             this.setStyle("bottom", this.addUnits(bottom));
8804             return this;
8805         },
8806
8807         /**
8808          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8809          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8810          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8811          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8812          * @return {Roo.Element} this
8813          */
8814         setXY : function(pos, animate){
8815             if(!animate || !A){
8816                 D.setXY(this.dom, pos);
8817             }else{
8818                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8819             }
8820             return this;
8821         },
8822
8823         /**
8824          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8825          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8826          * @param {Number} x X value for new position (coordinates are page-based)
8827          * @param {Number} y Y value for new position (coordinates are page-based)
8828          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8829          * @return {Roo.Element} this
8830          */
8831         setLocation : function(x, y, animate){
8832             this.setXY([x, y], this.preanim(arguments, 2));
8833             return this;
8834         },
8835
8836         /**
8837          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8838          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8839          * @param {Number} x X value for new position (coordinates are page-based)
8840          * @param {Number} y Y value for new position (coordinates are page-based)
8841          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8842          * @return {Roo.Element} this
8843          */
8844         moveTo : function(x, y, animate){
8845             this.setXY([x, y], this.preanim(arguments, 2));
8846             return this;
8847         },
8848
8849         /**
8850          * Returns the region of the given element.
8851          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8852          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8853          */
8854         getRegion : function(){
8855             return D.getRegion(this.dom);
8856         },
8857
8858         /**
8859          * Returns the offset height of the element
8860          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8861          * @return {Number} The element's height
8862          */
8863         getHeight : function(contentHeight){
8864             var h = this.dom.offsetHeight || 0;
8865             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8866         },
8867
8868         /**
8869          * Returns the offset width of the element
8870          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8871          * @return {Number} The element's width
8872          */
8873         getWidth : function(contentWidth){
8874             var w = this.dom.offsetWidth || 0;
8875             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8876         },
8877
8878         /**
8879          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8880          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8881          * if a height has not been set using CSS.
8882          * @return {Number}
8883          */
8884         getComputedHeight : function(){
8885             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8886             if(!h){
8887                 h = parseInt(this.getStyle('height'), 10) || 0;
8888                 if(!this.isBorderBox()){
8889                     h += this.getFrameWidth('tb');
8890                 }
8891             }
8892             return h;
8893         },
8894
8895         /**
8896          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8897          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8898          * if a width has not been set using CSS.
8899          * @return {Number}
8900          */
8901         getComputedWidth : function(){
8902             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8903             if(!w){
8904                 w = parseInt(this.getStyle('width'), 10) || 0;
8905                 if(!this.isBorderBox()){
8906                     w += this.getFrameWidth('lr');
8907                 }
8908             }
8909             return w;
8910         },
8911
8912         /**
8913          * Returns the size of the element.
8914          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8915          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8916          */
8917         getSize : function(contentSize){
8918             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8919         },
8920
8921         /**
8922          * Returns the width and height of the viewport.
8923          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8924          */
8925         getViewSize : function(){
8926             var d = this.dom, doc = document, aw = 0, ah = 0;
8927             if(d == doc || d == doc.body){
8928                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8929             }else{
8930                 return {
8931                     width : d.clientWidth,
8932                     height: d.clientHeight
8933                 };
8934             }
8935         },
8936
8937         /**
8938          * Returns the value of the "value" attribute
8939          * @param {Boolean} asNumber true to parse the value as a number
8940          * @return {String/Number}
8941          */
8942         getValue : function(asNumber){
8943             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8944         },
8945
8946         // private
8947         adjustWidth : function(width){
8948             if(typeof width == "number"){
8949                 if(this.autoBoxAdjust && !this.isBorderBox()){
8950                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8951                 }
8952                 if(width < 0){
8953                     width = 0;
8954                 }
8955             }
8956             return width;
8957         },
8958
8959         // private
8960         adjustHeight : function(height){
8961             if(typeof height == "number"){
8962                if(this.autoBoxAdjust && !this.isBorderBox()){
8963                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8964                }
8965                if(height < 0){
8966                    height = 0;
8967                }
8968             }
8969             return height;
8970         },
8971
8972         /**
8973          * Set the width of the element
8974          * @param {Number} width The new width
8975          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8976          * @return {Roo.Element} this
8977          */
8978         setWidth : function(width, animate){
8979             width = this.adjustWidth(width);
8980             if(!animate || !A){
8981                 this.dom.style.width = this.addUnits(width);
8982             }else{
8983                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8984             }
8985             return this;
8986         },
8987
8988         /**
8989          * Set the height of the element
8990          * @param {Number} height The new height
8991          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8992          * @return {Roo.Element} this
8993          */
8994          setHeight : function(height, animate){
8995             height = this.adjustHeight(height);
8996             if(!animate || !A){
8997                 this.dom.style.height = this.addUnits(height);
8998             }else{
8999                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9000             }
9001             return this;
9002         },
9003
9004         /**
9005          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9006          * @param {Number} width The new width
9007          * @param {Number} height The new height
9008          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9009          * @return {Roo.Element} this
9010          */
9011          setSize : function(width, height, animate){
9012             if(typeof width == "object"){ // in case of object from getSize()
9013                 height = width.height; width = width.width;
9014             }
9015             width = this.adjustWidth(width); height = this.adjustHeight(height);
9016             if(!animate || !A){
9017                 this.dom.style.width = this.addUnits(width);
9018                 this.dom.style.height = this.addUnits(height);
9019             }else{
9020                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9021             }
9022             return this;
9023         },
9024
9025         /**
9026          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9027          * @param {Number} x X value for new position (coordinates are page-based)
9028          * @param {Number} y Y value for new position (coordinates are page-based)
9029          * @param {Number} width The new width
9030          * @param {Number} height The new height
9031          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9032          * @return {Roo.Element} this
9033          */
9034         setBounds : function(x, y, width, height, animate){
9035             if(!animate || !A){
9036                 this.setSize(width, height);
9037                 this.setLocation(x, y);
9038             }else{
9039                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9040                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9041                               this.preanim(arguments, 4), 'motion');
9042             }
9043             return this;
9044         },
9045
9046         /**
9047          * 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.
9048          * @param {Roo.lib.Region} region The region to fill
9049          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9050          * @return {Roo.Element} this
9051          */
9052         setRegion : function(region, animate){
9053             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9054             return this;
9055         },
9056
9057         /**
9058          * Appends an event handler
9059          *
9060          * @param {String}   eventName     The type of event to append
9061          * @param {Function} fn        The method the event invokes
9062          * @param {Object} scope       (optional) The scope (this object) of the fn
9063          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9064          */
9065         addListener : function(eventName, fn, scope, options)
9066         {
9067             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9068                 this.addListener('touchstart', this.onTapHandler, this);
9069             }
9070             
9071             // we need to handle a special case where dom element is a svg element.
9072             // in this case we do not actua
9073             if (!this.dom) {
9074                 return;
9075             }
9076             
9077             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9078                 if (typeof(this.listeners[eventName]) == 'undefined') {
9079                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9080                 }
9081                 this.listeners[eventName].addListener(fn, scope, options);
9082                 return;
9083             }
9084             
9085                 
9086             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9087             
9088             
9089         },
9090         tapedTwice : false,
9091         onTapHandler : function(event)
9092         {
9093             if(!this.tapedTwice) {
9094                 this.tapedTwice = true;
9095                 var s = this;
9096                 setTimeout( function() {
9097                     s.tapedTwice = false;
9098                 }, 300 );
9099                 return;
9100             }
9101             event.preventDefault();
9102             var revent = new MouseEvent('dblclick',  {
9103                 view: window,
9104                 bubbles: true,
9105                 cancelable: true
9106             });
9107              
9108             this.dom.dispatchEvent(revent);
9109             //action on double tap goes below
9110              
9111         }, 
9112  
9113         /**
9114          * Removes an event handler from this element
9115          * @param {String} eventName the type of event to remove
9116          * @param {Function} fn the method the event invokes
9117          * @param {Function} scope (needed for svg fake listeners)
9118          * @return {Roo.Element} this
9119          */
9120         removeListener : function(eventName, fn, scope){
9121             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9122             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9123                 return this;
9124             }
9125             this.listeners[eventName].removeListener(fn, scope);
9126             return this;
9127         },
9128
9129         /**
9130          * Removes all previous added listeners from this element
9131          * @return {Roo.Element} this
9132          */
9133         removeAllListeners : function(){
9134             E.purgeElement(this.dom);
9135             this.listeners = {};
9136             return this;
9137         },
9138
9139         relayEvent : function(eventName, observable){
9140             this.on(eventName, function(e){
9141                 observable.fireEvent(eventName, e);
9142             });
9143         },
9144
9145         
9146         /**
9147          * Set the opacity of the element
9148          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9149          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9150          * @return {Roo.Element} this
9151          */
9152          setOpacity : function(opacity, animate){
9153             if(!animate || !A){
9154                 var s = this.dom.style;
9155                 if(Roo.isIE){
9156                     s.zoom = 1;
9157                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9158                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9159                 }else{
9160                     s.opacity = opacity;
9161                 }
9162             }else{
9163                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9164             }
9165             return this;
9166         },
9167
9168         /**
9169          * Gets the left X coordinate
9170          * @param {Boolean} local True to get the local css position instead of page coordinate
9171          * @return {Number}
9172          */
9173         getLeft : function(local){
9174             if(!local){
9175                 return this.getX();
9176             }else{
9177                 return parseInt(this.getStyle("left"), 10) || 0;
9178             }
9179         },
9180
9181         /**
9182          * Gets the right X coordinate of the element (element X position + element width)
9183          * @param {Boolean} local True to get the local css position instead of page coordinate
9184          * @return {Number}
9185          */
9186         getRight : function(local){
9187             if(!local){
9188                 return this.getX() + this.getWidth();
9189             }else{
9190                 return (this.getLeft(true) + this.getWidth()) || 0;
9191             }
9192         },
9193
9194         /**
9195          * Gets the top Y coordinate
9196          * @param {Boolean} local True to get the local css position instead of page coordinate
9197          * @return {Number}
9198          */
9199         getTop : function(local) {
9200             if(!local){
9201                 return this.getY();
9202             }else{
9203                 return parseInt(this.getStyle("top"), 10) || 0;
9204             }
9205         },
9206
9207         /**
9208          * Gets the bottom Y coordinate of the element (element Y position + element height)
9209          * @param {Boolean} local True to get the local css position instead of page coordinate
9210          * @return {Number}
9211          */
9212         getBottom : function(local){
9213             if(!local){
9214                 return this.getY() + this.getHeight();
9215             }else{
9216                 return (this.getTop(true) + this.getHeight()) || 0;
9217             }
9218         },
9219
9220         /**
9221         * Initializes positioning on this element. If a desired position is not passed, it will make the
9222         * the element positioned relative IF it is not already positioned.
9223         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9224         * @param {Number} zIndex (optional) The zIndex to apply
9225         * @param {Number} x (optional) Set the page X position
9226         * @param {Number} y (optional) Set the page Y position
9227         */
9228         position : function(pos, zIndex, x, y){
9229             if(!pos){
9230                if(this.getStyle('position') == 'static'){
9231                    this.setStyle('position', 'relative');
9232                }
9233             }else{
9234                 this.setStyle("position", pos);
9235             }
9236             if(zIndex){
9237                 this.setStyle("z-index", zIndex);
9238             }
9239             if(x !== undefined && y !== undefined){
9240                 this.setXY([x, y]);
9241             }else if(x !== undefined){
9242                 this.setX(x);
9243             }else if(y !== undefined){
9244                 this.setY(y);
9245             }
9246         },
9247
9248         /**
9249         * Clear positioning back to the default when the document was loaded
9250         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9251         * @return {Roo.Element} this
9252          */
9253         clearPositioning : function(value){
9254             value = value ||'';
9255             this.setStyle({
9256                 "left": value,
9257                 "right": value,
9258                 "top": value,
9259                 "bottom": value,
9260                 "z-index": "",
9261                 "position" : "static"
9262             });
9263             return this;
9264         },
9265
9266         /**
9267         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9268         * snapshot before performing an update and then restoring the element.
9269         * @return {Object}
9270         */
9271         getPositioning : function(){
9272             var l = this.getStyle("left");
9273             var t = this.getStyle("top");
9274             return {
9275                 "position" : this.getStyle("position"),
9276                 "left" : l,
9277                 "right" : l ? "" : this.getStyle("right"),
9278                 "top" : t,
9279                 "bottom" : t ? "" : this.getStyle("bottom"),
9280                 "z-index" : this.getStyle("z-index")
9281             };
9282         },
9283
9284         /**
9285          * Gets the width of the border(s) for the specified side(s)
9286          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9287          * passing lr would get the border (l)eft width + the border (r)ight width.
9288          * @return {Number} The width of the sides passed added together
9289          */
9290         getBorderWidth : function(side){
9291             return this.addStyles(side, El.borders);
9292         },
9293
9294         /**
9295          * Gets the width of the padding(s) for the specified side(s)
9296          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9297          * passing lr would get the padding (l)eft + the padding (r)ight.
9298          * @return {Number} The padding of the sides passed added together
9299          */
9300         getPadding : function(side){
9301             return this.addStyles(side, El.paddings);
9302         },
9303
9304         /**
9305         * Set positioning with an object returned by getPositioning().
9306         * @param {Object} posCfg
9307         * @return {Roo.Element} this
9308          */
9309         setPositioning : function(pc){
9310             this.applyStyles(pc);
9311             if(pc.right == "auto"){
9312                 this.dom.style.right = "";
9313             }
9314             if(pc.bottom == "auto"){
9315                 this.dom.style.bottom = "";
9316             }
9317             return this;
9318         },
9319
9320         // private
9321         fixDisplay : function(){
9322             if(this.getStyle("display") == "none"){
9323                 this.setStyle("visibility", "hidden");
9324                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9325                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9326                     this.setStyle("display", "block");
9327                 }
9328             }
9329         },
9330
9331         /**
9332          * Quick set left and top adding default units
9333          * @param {String} left The left CSS property value
9334          * @param {String} top The top CSS property value
9335          * @return {Roo.Element} this
9336          */
9337          setLeftTop : function(left, top){
9338             this.dom.style.left = this.addUnits(left);
9339             this.dom.style.top = this.addUnits(top);
9340             return this;
9341         },
9342
9343         /**
9344          * Move this element relative to its current position.
9345          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9346          * @param {Number} distance How far to move the element in pixels
9347          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9348          * @return {Roo.Element} this
9349          */
9350          move : function(direction, distance, animate){
9351             var xy = this.getXY();
9352             direction = direction.toLowerCase();
9353             switch(direction){
9354                 case "l":
9355                 case "left":
9356                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9357                     break;
9358                case "r":
9359                case "right":
9360                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9361                     break;
9362                case "t":
9363                case "top":
9364                case "up":
9365                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9366                     break;
9367                case "b":
9368                case "bottom":
9369                case "down":
9370                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9371                     break;
9372             }
9373             return this;
9374         },
9375
9376         /**
9377          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9378          * @return {Roo.Element} this
9379          */
9380         clip : function(){
9381             if(!this.isClipped){
9382                this.isClipped = true;
9383                this.originalClip = {
9384                    "o": this.getStyle("overflow"),
9385                    "x": this.getStyle("overflow-x"),
9386                    "y": this.getStyle("overflow-y")
9387                };
9388                this.setStyle("overflow", "hidden");
9389                this.setStyle("overflow-x", "hidden");
9390                this.setStyle("overflow-y", "hidden");
9391             }
9392             return this;
9393         },
9394
9395         /**
9396          *  Return clipping (overflow) to original clipping before clip() was called
9397          * @return {Roo.Element} this
9398          */
9399         unclip : function(){
9400             if(this.isClipped){
9401                 this.isClipped = false;
9402                 var o = this.originalClip;
9403                 if(o.o){this.setStyle("overflow", o.o);}
9404                 if(o.x){this.setStyle("overflow-x", o.x);}
9405                 if(o.y){this.setStyle("overflow-y", o.y);}
9406             }
9407             return this;
9408         },
9409
9410
9411         /**
9412          * Gets the x,y coordinates specified by the anchor position on the element.
9413          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9414          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9415          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9416          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9417          * @return {Array} [x, y] An array containing the element's x and y coordinates
9418          */
9419         getAnchorXY : function(anchor, local, s){
9420             //Passing a different size is useful for pre-calculating anchors,
9421             //especially for anchored animations that change the el size.
9422
9423             var w, h, vp = false;
9424             if(!s){
9425                 var d = this.dom;
9426                 if(d == document.body || d == document){
9427                     vp = true;
9428                     w = D.getViewWidth(); h = D.getViewHeight();
9429                 }else{
9430                     w = this.getWidth(); h = this.getHeight();
9431                 }
9432             }else{
9433                 w = s.width;  h = s.height;
9434             }
9435             var x = 0, y = 0, r = Math.round;
9436             switch((anchor || "tl").toLowerCase()){
9437                 case "c":
9438                     x = r(w*.5);
9439                     y = r(h*.5);
9440                 break;
9441                 case "t":
9442                     x = r(w*.5);
9443                     y = 0;
9444                 break;
9445                 case "l":
9446                     x = 0;
9447                     y = r(h*.5);
9448                 break;
9449                 case "r":
9450                     x = w;
9451                     y = r(h*.5);
9452                 break;
9453                 case "b":
9454                     x = r(w*.5);
9455                     y = h;
9456                 break;
9457                 case "tl":
9458                     x = 0;
9459                     y = 0;
9460                 break;
9461                 case "bl":
9462                     x = 0;
9463                     y = h;
9464                 break;
9465                 case "br":
9466                     x = w;
9467                     y = h;
9468                 break;
9469                 case "tr":
9470                     x = w;
9471                     y = 0;
9472                 break;
9473             }
9474             if(local === true){
9475                 return [x, y];
9476             }
9477             if(vp){
9478                 var sc = this.getScroll();
9479                 return [x + sc.left, y + sc.top];
9480             }
9481             //Add the element's offset xy
9482             var o = this.getXY();
9483             return [x+o[0], y+o[1]];
9484         },
9485
9486         /**
9487          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9488          * supported position values.
9489          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9490          * @param {String} position The position to align to.
9491          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9492          * @return {Array} [x, y]
9493          */
9494         getAlignToXY : function(el, p, o)
9495         {
9496             el = Roo.get(el);
9497             var d = this.dom;
9498             if(!el.dom){
9499                 throw "Element.alignTo with an element that doesn't exist";
9500             }
9501             var c = false; //constrain to viewport
9502             var p1 = "", p2 = "";
9503             o = o || [0,0];
9504
9505             if(!p){
9506                 p = "tl-bl";
9507             }else if(p == "?"){
9508                 p = "tl-bl?";
9509             }else if(p.indexOf("-") == -1){
9510                 p = "tl-" + p;
9511             }
9512             p = p.toLowerCase();
9513             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9514             if(!m){
9515                throw "Element.alignTo with an invalid alignment " + p;
9516             }
9517             p1 = m[1]; p2 = m[2]; c = !!m[3];
9518
9519             //Subtract the aligned el's internal xy from the target's offset xy
9520             //plus custom offset to get the aligned el's new offset xy
9521             var a1 = this.getAnchorXY(p1, true);
9522             var a2 = el.getAnchorXY(p2, false);
9523             var x = a2[0] - a1[0] + o[0];
9524             var y = a2[1] - a1[1] + o[1];
9525             if(c){
9526                 //constrain the aligned el to viewport if necessary
9527                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9528                 // 5px of margin for ie
9529                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9530
9531                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9532                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9533                 //otherwise swap the aligned el to the opposite border of the target.
9534                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9535                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9536                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9537                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9538
9539                var doc = document;
9540                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9541                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9542
9543                if((x+w) > dw + scrollX){
9544                     x = swapX ? r.left-w : dw+scrollX-w;
9545                 }
9546                if(x < scrollX){
9547                    x = swapX ? r.right : scrollX;
9548                }
9549                if((y+h) > dh + scrollY){
9550                     y = swapY ? r.top-h : dh+scrollY-h;
9551                 }
9552                if (y < scrollY){
9553                    y = swapY ? r.bottom : scrollY;
9554                }
9555             }
9556             return [x,y];
9557         },
9558
9559         // private
9560         getConstrainToXY : function(){
9561             var os = {top:0, left:0, bottom:0, right: 0};
9562
9563             return function(el, local, offsets, proposedXY){
9564                 el = Roo.get(el);
9565                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9566
9567                 var vw, vh, vx = 0, vy = 0;
9568                 if(el.dom == document.body || el.dom == document){
9569                     vw = Roo.lib.Dom.getViewWidth();
9570                     vh = Roo.lib.Dom.getViewHeight();
9571                 }else{
9572                     vw = el.dom.clientWidth;
9573                     vh = el.dom.clientHeight;
9574                     if(!local){
9575                         var vxy = el.getXY();
9576                         vx = vxy[0];
9577                         vy = vxy[1];
9578                     }
9579                 }
9580
9581                 var s = el.getScroll();
9582
9583                 vx += offsets.left + s.left;
9584                 vy += offsets.top + s.top;
9585
9586                 vw -= offsets.right;
9587                 vh -= offsets.bottom;
9588
9589                 var vr = vx+vw;
9590                 var vb = vy+vh;
9591
9592                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9593                 var x = xy[0], y = xy[1];
9594                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9595
9596                 // only move it if it needs it
9597                 var moved = false;
9598
9599                 // first validate right/bottom
9600                 if((x + w) > vr){
9601                     x = vr - w;
9602                     moved = true;
9603                 }
9604                 if((y + h) > vb){
9605                     y = vb - h;
9606                     moved = true;
9607                 }
9608                 // then make sure top/left isn't negative
9609                 if(x < vx){
9610                     x = vx;
9611                     moved = true;
9612                 }
9613                 if(y < vy){
9614                     y = vy;
9615                     moved = true;
9616                 }
9617                 return moved ? [x, y] : false;
9618             };
9619         }(),
9620
9621         // private
9622         adjustForConstraints : function(xy, parent, offsets){
9623             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9624         },
9625
9626         /**
9627          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9628          * document it aligns it to the viewport.
9629          * The position parameter is optional, and can be specified in any one of the following formats:
9630          * <ul>
9631          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9632          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9633          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9634          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9635          *   <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
9636          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9637          * </ul>
9638          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9639          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9640          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9641          * that specified in order to enforce the viewport constraints.
9642          * Following are all of the supported anchor positions:
9643     <pre>
9644     Value  Description
9645     -----  -----------------------------
9646     tl     The top left corner (default)
9647     t      The center of the top edge
9648     tr     The top right corner
9649     l      The center of the left edge
9650     c      In the center of the element
9651     r      The center of the right edge
9652     bl     The bottom left corner
9653     b      The center of the bottom edge
9654     br     The bottom right corner
9655     </pre>
9656     Example Usage:
9657     <pre><code>
9658     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9659     el.alignTo("other-el");
9660
9661     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9662     el.alignTo("other-el", "tr?");
9663
9664     // align the bottom right corner of el with the center left edge of other-el
9665     el.alignTo("other-el", "br-l?");
9666
9667     // align the center of el with the bottom left corner of other-el and
9668     // adjust the x position by -6 pixels (and the y position by 0)
9669     el.alignTo("other-el", "c-bl", [-6, 0]);
9670     </code></pre>
9671          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9672          * @param {String} position The position to align to.
9673          * @param {Array} offsets (optional) Offset the positioning by [x, y]
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         alignTo : function(element, position, offsets, animate){
9678             var xy = this.getAlignToXY(element, position, offsets);
9679             this.setXY(xy, this.preanim(arguments, 3));
9680             return this;
9681         },
9682
9683         /**
9684          * Anchors an element to another element and realigns it when the window is resized.
9685          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9686          * @param {String} position The position to align to.
9687          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9688          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9689          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9690          * is a number, it is used as the buffer delay (defaults to 50ms).
9691          * @param {Function} callback The function to call after the animation finishes
9692          * @return {Roo.Element} this
9693          */
9694         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9695             var action = function(){
9696                 this.alignTo(el, alignment, offsets, animate);
9697                 Roo.callback(callback, this);
9698             };
9699             Roo.EventManager.onWindowResize(action, this);
9700             var tm = typeof monitorScroll;
9701             if(tm != 'undefined'){
9702                 Roo.EventManager.on(window, 'scroll', action, this,
9703                     {buffer: tm == 'number' ? monitorScroll : 50});
9704             }
9705             action.call(this); // align immediately
9706             return this;
9707         },
9708         /**
9709          * Clears any opacity settings from this element. Required in some cases for IE.
9710          * @return {Roo.Element} this
9711          */
9712         clearOpacity : function(){
9713             if (window.ActiveXObject) {
9714                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9715                     this.dom.style.filter = "";
9716                 }
9717             } else {
9718                 this.dom.style.opacity = "";
9719                 this.dom.style["-moz-opacity"] = "";
9720                 this.dom.style["-khtml-opacity"] = "";
9721             }
9722             return this;
9723         },
9724
9725         /**
9726          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9727          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9728          * @return {Roo.Element} this
9729          */
9730         hide : function(animate){
9731             this.setVisible(false, this.preanim(arguments, 0));
9732             return this;
9733         },
9734
9735         /**
9736         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9737         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9738          * @return {Roo.Element} this
9739          */
9740         show : function(animate){
9741             this.setVisible(true, this.preanim(arguments, 0));
9742             return this;
9743         },
9744
9745         /**
9746          * @private Test if size has a unit, otherwise appends the default
9747          */
9748         addUnits : function(size){
9749             return Roo.Element.addUnits(size, this.defaultUnit);
9750         },
9751
9752         /**
9753          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9754          * @return {Roo.Element} this
9755          */
9756         beginMeasure : function(){
9757             var el = this.dom;
9758             if(el.offsetWidth || el.offsetHeight){
9759                 return this; // offsets work already
9760             }
9761             var changed = [];
9762             var p = this.dom, b = document.body; // start with this element
9763             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9764                 var pe = Roo.get(p);
9765                 if(pe.getStyle('display') == 'none'){
9766                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9767                     p.style.visibility = "hidden";
9768                     p.style.display = "block";
9769                 }
9770                 p = p.parentNode;
9771             }
9772             this._measureChanged = changed;
9773             return this;
9774
9775         },
9776
9777         /**
9778          * Restores displays to before beginMeasure was called
9779          * @return {Roo.Element} this
9780          */
9781         endMeasure : function(){
9782             var changed = this._measureChanged;
9783             if(changed){
9784                 for(var i = 0, len = changed.length; i < len; i++) {
9785                     var r = changed[i];
9786                     r.el.style.visibility = r.visibility;
9787                     r.el.style.display = "none";
9788                 }
9789                 this._measureChanged = null;
9790             }
9791             return this;
9792         },
9793
9794         /**
9795         * Update the innerHTML of this element, optionally searching for and processing scripts
9796         * @param {String} html The new HTML
9797         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9798         * @param {Function} callback For async script loading you can be noticed when the update completes
9799         * @return {Roo.Element} this
9800          */
9801         update : function(html, loadScripts, callback){
9802             if(typeof html == "undefined"){
9803                 html = "";
9804             }
9805             if(loadScripts !== true){
9806                 this.dom.innerHTML = html;
9807                 if(typeof callback == "function"){
9808                     callback();
9809                 }
9810                 return this;
9811             }
9812             var id = Roo.id();
9813             var dom = this.dom;
9814
9815             html += '<span id="' + id + '"></span>';
9816
9817             E.onAvailable(id, function(){
9818                 var hd = document.getElementsByTagName("head")[0];
9819                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9820                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9821                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9822
9823                 var match;
9824                 while(match = re.exec(html)){
9825                     var attrs = match[1];
9826                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9827                     if(srcMatch && srcMatch[2]){
9828                        var s = document.createElement("script");
9829                        s.src = srcMatch[2];
9830                        var typeMatch = attrs.match(typeRe);
9831                        if(typeMatch && typeMatch[2]){
9832                            s.type = typeMatch[2];
9833                        }
9834                        hd.appendChild(s);
9835                     }else if(match[2] && match[2].length > 0){
9836                         if(window.execScript) {
9837                            window.execScript(match[2]);
9838                         } else {
9839                             /**
9840                              * eval:var:id
9841                              * eval:var:dom
9842                              * eval:var:html
9843                              * 
9844                              */
9845                            window.eval(match[2]);
9846                         }
9847                     }
9848                 }
9849                 var el = document.getElementById(id);
9850                 if(el){el.parentNode.removeChild(el);}
9851                 if(typeof callback == "function"){
9852                     callback();
9853                 }
9854             });
9855             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9856             return this;
9857         },
9858
9859         /**
9860          * Direct access to the UpdateManager update() method (takes the same parameters).
9861          * @param {String/Function} url The url for this request or a function to call to get the url
9862          * @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}
9863          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9864          * @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.
9865          * @return {Roo.Element} this
9866          */
9867         load : function(){
9868             var um = this.getUpdateManager();
9869             um.update.apply(um, arguments);
9870             return this;
9871         },
9872
9873         /**
9874         * Gets this element's UpdateManager
9875         * @return {Roo.UpdateManager} The UpdateManager
9876         */
9877         getUpdateManager : function(){
9878             if(!this.updateManager){
9879                 this.updateManager = new Roo.UpdateManager(this);
9880             }
9881             return this.updateManager;
9882         },
9883
9884         /**
9885          * Disables text selection for this element (normalized across browsers)
9886          * @return {Roo.Element} this
9887          */
9888         unselectable : function(){
9889             this.dom.unselectable = "on";
9890             this.swallowEvent("selectstart", true);
9891             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9892             this.addClass("x-unselectable");
9893             return this;
9894         },
9895
9896         /**
9897         * Calculates the x, y to center this element on the screen
9898         * @return {Array} The x, y values [x, y]
9899         */
9900         getCenterXY : function(){
9901             return this.getAlignToXY(document, 'c-c');
9902         },
9903
9904         /**
9905         * Centers the Element in either the viewport, or another Element.
9906         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9907         */
9908         center : function(centerIn){
9909             this.alignTo(centerIn || document, 'c-c');
9910             return this;
9911         },
9912
9913         /**
9914          * Tests various css rules/browsers to determine if this element uses a border box
9915          * @return {Boolean}
9916          */
9917         isBorderBox : function(){
9918             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9919         },
9920
9921         /**
9922          * Return a box {x, y, width, height} that can be used to set another elements
9923          * size/location to match this element.
9924          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9925          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9926          * @return {Object} box An object in the format {x, y, width, height}
9927          */
9928         getBox : function(contentBox, local){
9929             var xy;
9930             if(!local){
9931                 xy = this.getXY();
9932             }else{
9933                 var left = parseInt(this.getStyle("left"), 10) || 0;
9934                 var top = parseInt(this.getStyle("top"), 10) || 0;
9935                 xy = [left, top];
9936             }
9937             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9938             if(!contentBox){
9939                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9940             }else{
9941                 var l = this.getBorderWidth("l")+this.getPadding("l");
9942                 var r = this.getBorderWidth("r")+this.getPadding("r");
9943                 var t = this.getBorderWidth("t")+this.getPadding("t");
9944                 var b = this.getBorderWidth("b")+this.getPadding("b");
9945                 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)};
9946             }
9947             bx.right = bx.x + bx.width;
9948             bx.bottom = bx.y + bx.height;
9949             return bx;
9950         },
9951
9952         /**
9953          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9954          for more information about the sides.
9955          * @param {String} sides
9956          * @return {Number}
9957          */
9958         getFrameWidth : function(sides, onlyContentBox){
9959             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9960         },
9961
9962         /**
9963          * 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.
9964          * @param {Object} box The box to fill {x, y, width, height}
9965          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9966          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9967          * @return {Roo.Element} this
9968          */
9969         setBox : function(box, adjust, animate){
9970             var w = box.width, h = box.height;
9971             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9972                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9973                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9974             }
9975             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9976             return this;
9977         },
9978
9979         /**
9980          * Forces the browser to repaint this element
9981          * @return {Roo.Element} this
9982          */
9983          repaint : function(){
9984             var dom = this.dom;
9985             this.addClass("x-repaint");
9986             setTimeout(function(){
9987                 Roo.get(dom).removeClass("x-repaint");
9988             }, 1);
9989             return this;
9990         },
9991
9992         /**
9993          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9994          * then it returns the calculated width of the sides (see getPadding)
9995          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9996          * @return {Object/Number}
9997          */
9998         getMargins : function(side){
9999             if(!side){
10000                 return {
10001                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10002                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10003                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10004                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10005                 };
10006             }else{
10007                 return this.addStyles(side, El.margins);
10008              }
10009         },
10010
10011         // private
10012         addStyles : function(sides, styles){
10013             var val = 0, v, w;
10014             for(var i = 0, len = sides.length; i < len; i++){
10015                 v = this.getStyle(styles[sides.charAt(i)]);
10016                 if(v){
10017                      w = parseInt(v, 10);
10018                      if(w){ val += w; }
10019                 }
10020             }
10021             return val;
10022         },
10023
10024         /**
10025          * Creates a proxy element of this element
10026          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10027          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10028          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10029          * @return {Roo.Element} The new proxy element
10030          */
10031         createProxy : function(config, renderTo, matchBox){
10032             if(renderTo){
10033                 renderTo = Roo.getDom(renderTo);
10034             }else{
10035                 renderTo = document.body;
10036             }
10037             config = typeof config == "object" ?
10038                 config : {tag : "div", cls: config};
10039             var proxy = Roo.DomHelper.append(renderTo, config, true);
10040             if(matchBox){
10041                proxy.setBox(this.getBox());
10042             }
10043             return proxy;
10044         },
10045
10046         /**
10047          * Puts a mask over this element to disable user interaction. Requires core.css.
10048          * This method can only be applied to elements which accept child nodes.
10049          * @param {String} msg (optional) A message to display in the mask
10050          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10051          * @return {Element} The mask  element
10052          */
10053         mask : function(msg, msgCls)
10054         {
10055             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10056                 this.setStyle("position", "relative");
10057             }
10058             if(!this._mask){
10059                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10060             }
10061             
10062             this.addClass("x-masked");
10063             this._mask.setDisplayed(true);
10064             
10065             // we wander
10066             var z = 0;
10067             var dom = this.dom;
10068             while (dom && dom.style) {
10069                 if (!isNaN(parseInt(dom.style.zIndex))) {
10070                     z = Math.max(z, parseInt(dom.style.zIndex));
10071                 }
10072                 dom = dom.parentNode;
10073             }
10074             // if we are masking the body - then it hides everything..
10075             if (this.dom == document.body) {
10076                 z = 1000000;
10077                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10078                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10079             }
10080            
10081             if(typeof msg == 'string'){
10082                 if(!this._maskMsg){
10083                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10084                         cls: "roo-el-mask-msg", 
10085                         cn: [
10086                             {
10087                                 tag: 'i',
10088                                 cls: 'fa fa-spinner fa-spin'
10089                             },
10090                             {
10091                                 tag: 'div'
10092                             }   
10093                         ]
10094                     }, true);
10095                 }
10096                 var mm = this._maskMsg;
10097                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10098                 if (mm.dom.lastChild) { // weird IE issue?
10099                     mm.dom.lastChild.innerHTML = msg;
10100                 }
10101                 mm.setDisplayed(true);
10102                 mm.center(this);
10103                 mm.setStyle('z-index', z + 102);
10104             }
10105             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10106                 this._mask.setHeight(this.getHeight());
10107             }
10108             this._mask.setStyle('z-index', z + 100);
10109             
10110             return this._mask;
10111         },
10112
10113         /**
10114          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10115          * it is cached for reuse.
10116          */
10117         unmask : function(removeEl){
10118             if(this._mask){
10119                 if(removeEl === true){
10120                     this._mask.remove();
10121                     delete this._mask;
10122                     if(this._maskMsg){
10123                         this._maskMsg.remove();
10124                         delete this._maskMsg;
10125                     }
10126                 }else{
10127                     this._mask.setDisplayed(false);
10128                     if(this._maskMsg){
10129                         this._maskMsg.setDisplayed(false);
10130                     }
10131                 }
10132             }
10133             this.removeClass("x-masked");
10134         },
10135
10136         /**
10137          * Returns true if this element is masked
10138          * @return {Boolean}
10139          */
10140         isMasked : function(){
10141             return this._mask && this._mask.isVisible();
10142         },
10143
10144         /**
10145          * Creates an iframe shim for this element to keep selects and other windowed objects from
10146          * showing through.
10147          * @return {Roo.Element} The new shim element
10148          */
10149         createShim : function(){
10150             var el = document.createElement('iframe');
10151             el.frameBorder = 'no';
10152             el.className = 'roo-shim';
10153             if(Roo.isIE && Roo.isSecure){
10154                 el.src = Roo.SSL_SECURE_URL;
10155             }
10156             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10157             shim.autoBoxAdjust = false;
10158             return shim;
10159         },
10160
10161         /**
10162          * Removes this element from the DOM and deletes it from the cache
10163          */
10164         remove : function(){
10165             if(this.dom.parentNode){
10166                 this.dom.parentNode.removeChild(this.dom);
10167             }
10168             delete El.cache[this.dom.id];
10169         },
10170
10171         /**
10172          * Sets up event handlers to add and remove a css class when the mouse is over this element
10173          * @param {String} className
10174          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10175          * mouseout events for children elements
10176          * @return {Roo.Element} this
10177          */
10178         addClassOnOver : function(className, preventFlicker){
10179             this.on("mouseover", function(){
10180                 Roo.fly(this, '_internal').addClass(className);
10181             }, this.dom);
10182             var removeFn = function(e){
10183                 if(preventFlicker !== true || !e.within(this, true)){
10184                     Roo.fly(this, '_internal').removeClass(className);
10185                 }
10186             };
10187             this.on("mouseout", removeFn, this.dom);
10188             return this;
10189         },
10190
10191         /**
10192          * Sets up event handlers to add and remove a css class when this element has the focus
10193          * @param {String} className
10194          * @return {Roo.Element} this
10195          */
10196         addClassOnFocus : function(className){
10197             this.on("focus", function(){
10198                 Roo.fly(this, '_internal').addClass(className);
10199             }, this.dom);
10200             this.on("blur", function(){
10201                 Roo.fly(this, '_internal').removeClass(className);
10202             }, this.dom);
10203             return this;
10204         },
10205         /**
10206          * 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)
10207          * @param {String} className
10208          * @return {Roo.Element} this
10209          */
10210         addClassOnClick : function(className){
10211             var dom = this.dom;
10212             this.on("mousedown", function(){
10213                 Roo.fly(dom, '_internal').addClass(className);
10214                 var d = Roo.get(document);
10215                 var fn = function(){
10216                     Roo.fly(dom, '_internal').removeClass(className);
10217                     d.removeListener("mouseup", fn);
10218                 };
10219                 d.on("mouseup", fn);
10220             });
10221             return this;
10222         },
10223
10224         /**
10225          * Stops the specified event from bubbling and optionally prevents the default action
10226          * @param {String} eventName
10227          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10228          * @return {Roo.Element} this
10229          */
10230         swallowEvent : function(eventName, preventDefault){
10231             var fn = function(e){
10232                 e.stopPropagation();
10233                 if(preventDefault){
10234                     e.preventDefault();
10235                 }
10236             };
10237             if(eventName instanceof Array){
10238                 for(var i = 0, len = eventName.length; i < len; i++){
10239                      this.on(eventName[i], fn);
10240                 }
10241                 return this;
10242             }
10243             this.on(eventName, fn);
10244             return this;
10245         },
10246
10247         /**
10248          * @private
10249          */
10250         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10251
10252         /**
10253          * Sizes this element to its parent element's dimensions performing
10254          * neccessary box adjustments.
10255          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10256          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10257          * @return {Roo.Element} this
10258          */
10259         fitToParent : function(monitorResize, targetParent) {
10260           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10261           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10262           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10263             return this;
10264           }
10265           var p = Roo.get(targetParent || this.dom.parentNode);
10266           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10267           if (monitorResize === true) {
10268             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10269             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10270           }
10271           return this;
10272         },
10273
10274         /**
10275          * Gets the next sibling, skipping text nodes
10276          * @return {HTMLElement} The next sibling or null
10277          */
10278         getNextSibling : function(){
10279             var n = this.dom.nextSibling;
10280             while(n && n.nodeType != 1){
10281                 n = n.nextSibling;
10282             }
10283             return n;
10284         },
10285
10286         /**
10287          * Gets the previous sibling, skipping text nodes
10288          * @return {HTMLElement} The previous sibling or null
10289          */
10290         getPrevSibling : function(){
10291             var n = this.dom.previousSibling;
10292             while(n && n.nodeType != 1){
10293                 n = n.previousSibling;
10294             }
10295             return n;
10296         },
10297
10298
10299         /**
10300          * Appends the passed element(s) to this element
10301          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10302          * @return {Roo.Element} this
10303          */
10304         appendChild: function(el){
10305             el = Roo.get(el);
10306             el.appendTo(this);
10307             return this;
10308         },
10309
10310         /**
10311          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10312          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10313          * automatically generated with the specified attributes.
10314          * @param {HTMLElement} insertBefore (optional) a child element of this element
10315          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10316          * @return {Roo.Element} The new child element
10317          */
10318         createChild: function(config, insertBefore, returnDom){
10319             config = config || {tag:'div'};
10320             if(insertBefore){
10321                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10322             }
10323             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10324         },
10325
10326         /**
10327          * Appends this element to the passed element
10328          * @param {String/HTMLElement/Element} el The new parent element
10329          * @return {Roo.Element} this
10330          */
10331         appendTo: function(el){
10332             el = Roo.getDom(el);
10333             el.appendChild(this.dom);
10334             return this;
10335         },
10336
10337         /**
10338          * Inserts this element before the passed element in the DOM
10339          * @param {String/HTMLElement/Element} el The element to insert before
10340          * @return {Roo.Element} this
10341          */
10342         insertBefore: function(el){
10343             el = Roo.getDom(el);
10344             el.parentNode.insertBefore(this.dom, el);
10345             return this;
10346         },
10347
10348         /**
10349          * Inserts this element after the passed element in the DOM
10350          * @param {String/HTMLElement/Element} el The element to insert after
10351          * @return {Roo.Element} this
10352          */
10353         insertAfter: function(el){
10354             el = Roo.getDom(el);
10355             el.parentNode.insertBefore(this.dom, el.nextSibling);
10356             return this;
10357         },
10358
10359         /**
10360          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10361          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10362          * @return {Roo.Element} The new child
10363          */
10364         insertFirst: function(el, returnDom){
10365             el = el || {};
10366             if(typeof el == 'object' && !el.nodeType){ // dh config
10367                 return this.createChild(el, this.dom.firstChild, returnDom);
10368             }else{
10369                 el = Roo.getDom(el);
10370                 this.dom.insertBefore(el, this.dom.firstChild);
10371                 return !returnDom ? Roo.get(el) : el;
10372             }
10373         },
10374
10375         /**
10376          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10377          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10378          * @param {String} where (optional) 'before' or 'after' defaults to before
10379          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10380          * @return {Roo.Element} the inserted Element
10381          */
10382         insertSibling: function(el, where, returnDom){
10383             where = where ? where.toLowerCase() : 'before';
10384             el = el || {};
10385             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10386
10387             if(typeof el == 'object' && !el.nodeType){ // dh config
10388                 if(where == 'after' && !this.dom.nextSibling){
10389                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10390                 }else{
10391                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10392                 }
10393
10394             }else{
10395                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10396                             where == 'before' ? this.dom : this.dom.nextSibling);
10397                 if(!returnDom){
10398                     rt = Roo.get(rt);
10399                 }
10400             }
10401             return rt;
10402         },
10403
10404         /**
10405          * Creates and wraps this element with another element
10406          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10407          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10408          * @return {HTMLElement/Element} The newly created wrapper element
10409          */
10410         wrap: function(config, returnDom){
10411             if(!config){
10412                 config = {tag: "div"};
10413             }
10414             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10415             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10416             return newEl;
10417         },
10418
10419         /**
10420          * Replaces the passed element with this element
10421          * @param {String/HTMLElement/Element} el The element to replace
10422          * @return {Roo.Element} this
10423          */
10424         replace: function(el){
10425             el = Roo.get(el);
10426             this.insertBefore(el);
10427             el.remove();
10428             return this;
10429         },
10430
10431         /**
10432          * Inserts an html fragment into this element
10433          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10434          * @param {String} html The HTML fragment
10435          * @param {Boolean} returnEl True to return an Roo.Element
10436          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10437          */
10438         insertHtml : function(where, html, returnEl){
10439             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10440             return returnEl ? Roo.get(el) : el;
10441         },
10442
10443         /**
10444          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10445          * @param {Object} o The object with the attributes
10446          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10447          * @return {Roo.Element} this
10448          */
10449         set : function(o, useSet){
10450             var el = this.dom;
10451             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10452             for(var attr in o){
10453                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10454                 if(attr=="cls"){
10455                     el.className = o["cls"];
10456                 }else{
10457                     if(useSet) {
10458                         el.setAttribute(attr, o[attr]);
10459                     } else {
10460                         el[attr] = o[attr];
10461                     }
10462                 }
10463             }
10464             if(o.style){
10465                 Roo.DomHelper.applyStyles(el, o.style);
10466             }
10467             return this;
10468         },
10469
10470         /**
10471          * Convenience method for constructing a KeyMap
10472          * @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:
10473          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10474          * @param {Function} fn The function to call
10475          * @param {Object} scope (optional) The scope of the function
10476          * @return {Roo.KeyMap} The KeyMap created
10477          */
10478         addKeyListener : function(key, fn, scope){
10479             var config;
10480             if(typeof key != "object" || key instanceof Array){
10481                 config = {
10482                     key: key,
10483                     fn: fn,
10484                     scope: scope
10485                 };
10486             }else{
10487                 config = {
10488                     key : key.key,
10489                     shift : key.shift,
10490                     ctrl : key.ctrl,
10491                     alt : key.alt,
10492                     fn: fn,
10493                     scope: scope
10494                 };
10495             }
10496             return new Roo.KeyMap(this, config);
10497         },
10498
10499         /**
10500          * Creates a KeyMap for this element
10501          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10502          * @return {Roo.KeyMap} The KeyMap created
10503          */
10504         addKeyMap : function(config){
10505             return new Roo.KeyMap(this, config);
10506         },
10507
10508         /**
10509          * Returns true if this element is scrollable.
10510          * @return {Boolean}
10511          */
10512          isScrollable : function(){
10513             var dom = this.dom;
10514             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10515         },
10516
10517         /**
10518          * 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().
10519          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10520          * @param {Number} value The new scroll value
10521          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10522          * @return {Element} this
10523          */
10524
10525         scrollTo : function(side, value, animate){
10526             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10527             if(!animate || !A){
10528                 this.dom[prop] = value;
10529             }else{
10530                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10531                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10532             }
10533             return this;
10534         },
10535
10536         /**
10537          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10538          * within this element's scrollable range.
10539          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10540          * @param {Number} distance How far to scroll the element in pixels
10541          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10542          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10543          * was scrolled as far as it could go.
10544          */
10545          scroll : function(direction, distance, animate){
10546              if(!this.isScrollable()){
10547                  return;
10548              }
10549              var el = this.dom;
10550              var l = el.scrollLeft, t = el.scrollTop;
10551              var w = el.scrollWidth, h = el.scrollHeight;
10552              var cw = el.clientWidth, ch = el.clientHeight;
10553              direction = direction.toLowerCase();
10554              var scrolled = false;
10555              var a = this.preanim(arguments, 2);
10556              switch(direction){
10557                  case "l":
10558                  case "left":
10559                      if(w - l > cw){
10560                          var v = Math.min(l + distance, w-cw);
10561                          this.scrollTo("left", v, a);
10562                          scrolled = true;
10563                      }
10564                      break;
10565                 case "r":
10566                 case "right":
10567                      if(l > 0){
10568                          var v = Math.max(l - distance, 0);
10569                          this.scrollTo("left", v, a);
10570                          scrolled = true;
10571                      }
10572                      break;
10573                 case "t":
10574                 case "top":
10575                 case "up":
10576                      if(t > 0){
10577                          var v = Math.max(t - distance, 0);
10578                          this.scrollTo("top", v, a);
10579                          scrolled = true;
10580                      }
10581                      break;
10582                 case "b":
10583                 case "bottom":
10584                 case "down":
10585                      if(h - t > ch){
10586                          var v = Math.min(t + distance, h-ch);
10587                          this.scrollTo("top", v, a);
10588                          scrolled = true;
10589                      }
10590                      break;
10591              }
10592              return scrolled;
10593         },
10594
10595         /**
10596          * Translates the passed page coordinates into left/top css values for this element
10597          * @param {Number/Array} x The page x or an array containing [x, y]
10598          * @param {Number} y The page y
10599          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10600          */
10601         translatePoints : function(x, y){
10602             if(typeof x == 'object' || x instanceof Array){
10603                 y = x[1]; x = x[0];
10604             }
10605             var p = this.getStyle('position');
10606             var o = this.getXY();
10607
10608             var l = parseInt(this.getStyle('left'), 10);
10609             var t = parseInt(this.getStyle('top'), 10);
10610
10611             if(isNaN(l)){
10612                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10613             }
10614             if(isNaN(t)){
10615                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10616             }
10617
10618             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10619         },
10620
10621         /**
10622          * Returns the current scroll position of the element.
10623          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10624          */
10625         getScroll : function(){
10626             var d = this.dom, doc = document;
10627             if(d == doc || d == doc.body){
10628                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10629                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10630                 return {left: l, top: t};
10631             }else{
10632                 return {left: d.scrollLeft, top: d.scrollTop};
10633             }
10634         },
10635
10636         /**
10637          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10638          * are convert to standard 6 digit hex color.
10639          * @param {String} attr The css attribute
10640          * @param {String} defaultValue The default value to use when a valid color isn't found
10641          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10642          * YUI color anims.
10643          */
10644         getColor : function(attr, defaultValue, prefix){
10645             var v = this.getStyle(attr);
10646             if(!v || v == "transparent" || v == "inherit") {
10647                 return defaultValue;
10648             }
10649             var color = typeof prefix == "undefined" ? "#" : prefix;
10650             if(v.substr(0, 4) == "rgb("){
10651                 var rvs = v.slice(4, v.length -1).split(",");
10652                 for(var i = 0; i < 3; i++){
10653                     var h = parseInt(rvs[i]).toString(16);
10654                     if(h < 16){
10655                         h = "0" + h;
10656                     }
10657                     color += h;
10658                 }
10659             } else {
10660                 if(v.substr(0, 1) == "#"){
10661                     if(v.length == 4) {
10662                         for(var i = 1; i < 4; i++){
10663                             var c = v.charAt(i);
10664                             color +=  c + c;
10665                         }
10666                     }else if(v.length == 7){
10667                         color += v.substr(1);
10668                     }
10669                 }
10670             }
10671             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10672         },
10673
10674         /**
10675          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10676          * gradient background, rounded corners and a 4-way shadow.
10677          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10678          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10679          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10680          * @return {Roo.Element} this
10681          */
10682         boxWrap : function(cls){
10683             cls = cls || 'x-box';
10684             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10685             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10686             return el;
10687         },
10688
10689         /**
10690          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10691          * @param {String} namespace The namespace in which to look for the attribute
10692          * @param {String} name The attribute name
10693          * @return {String} The attribute value
10694          */
10695         getAttributeNS : Roo.isIE ? function(ns, name){
10696             var d = this.dom;
10697             var type = typeof d[ns+":"+name];
10698             if(type != 'undefined' && type != 'unknown'){
10699                 return d[ns+":"+name];
10700             }
10701             return d[name];
10702         } : function(ns, name){
10703             var d = this.dom;
10704             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10705         },
10706         
10707         
10708         /**
10709          * Sets or Returns the value the dom attribute value
10710          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10711          * @param {String} value (optional) The value to set the attribute to
10712          * @return {String} The attribute value
10713          */
10714         attr : function(name){
10715             if (arguments.length > 1) {
10716                 this.dom.setAttribute(name, arguments[1]);
10717                 return arguments[1];
10718             }
10719             if (typeof(name) == 'object') {
10720                 for(var i in name) {
10721                     this.attr(i, name[i]);
10722                 }
10723                 return name;
10724             }
10725             
10726             
10727             if (!this.dom.hasAttribute(name)) {
10728                 return undefined;
10729             }
10730             return this.dom.getAttribute(name);
10731         }
10732         
10733         
10734         
10735     };
10736
10737     var ep = El.prototype;
10738
10739     /**
10740      * Appends an event handler (Shorthand for addListener)
10741      * @param {String}   eventName     The type of event to append
10742      * @param {Function} fn        The method the event invokes
10743      * @param {Object} scope       (optional) The scope (this object) of the fn
10744      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10745      * @method
10746      */
10747     ep.on = ep.addListener;
10748         // backwards compat
10749     ep.mon = ep.addListener;
10750
10751     /**
10752      * Removes an event handler from this element (shorthand for removeListener)
10753      * @param {String} eventName the type of event to remove
10754      * @param {Function} fn the method the event invokes
10755      * @return {Roo.Element} this
10756      * @method
10757      */
10758     ep.un = ep.removeListener;
10759
10760     /**
10761      * true to automatically adjust width and height settings for box-model issues (default to true)
10762      */
10763     ep.autoBoxAdjust = true;
10764
10765     // private
10766     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10767
10768     // private
10769     El.addUnits = function(v, defaultUnit){
10770         if(v === "" || v == "auto"){
10771             return v;
10772         }
10773         if(v === undefined){
10774             return '';
10775         }
10776         if(typeof v == "number" || !El.unitPattern.test(v)){
10777             return v + (defaultUnit || 'px');
10778         }
10779         return v;
10780     };
10781
10782     // special markup used throughout Roo when box wrapping elements
10783     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>';
10784     /**
10785      * Visibility mode constant - Use visibility to hide element
10786      * @static
10787      * @type Number
10788      */
10789     El.VISIBILITY = 1;
10790     /**
10791      * Visibility mode constant - Use display to hide element
10792      * @static
10793      * @type Number
10794      */
10795     El.DISPLAY = 2;
10796
10797     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10798     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10799     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10800
10801
10802
10803     /**
10804      * @private
10805      */
10806     El.cache = {};
10807
10808     var docEl;
10809
10810     /**
10811      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10812      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10813      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10814      * @return {Element} The Element object
10815      * @static
10816      */
10817     El.get = function(el){
10818         var ex, elm, id;
10819         if(!el){ return null; }
10820         if(typeof el == "string"){ // element id
10821             if(!(elm = document.getElementById(el))){
10822                 return null;
10823             }
10824             if(ex = El.cache[el]){
10825                 ex.dom = elm;
10826             }else{
10827                 ex = El.cache[el] = new El(elm);
10828             }
10829             return ex;
10830         }else if(el.tagName){ // dom element
10831             if(!(id = el.id)){
10832                 id = Roo.id(el);
10833             }
10834             if(ex = El.cache[id]){
10835                 ex.dom = el;
10836             }else{
10837                 ex = El.cache[id] = new El(el);
10838             }
10839             return ex;
10840         }else if(el instanceof El){
10841             if(el != docEl){
10842                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10843                                                               // catch case where it hasn't been appended
10844                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10845             }
10846             return el;
10847         }else if(el.isComposite){
10848             return el;
10849         }else if(el instanceof Array){
10850             return El.select(el);
10851         }else if(el == document){
10852             // create a bogus element object representing the document object
10853             if(!docEl){
10854                 var f = function(){};
10855                 f.prototype = El.prototype;
10856                 docEl = new f();
10857                 docEl.dom = document;
10858             }
10859             return docEl;
10860         }
10861         return null;
10862     };
10863
10864     // private
10865     El.uncache = function(el){
10866         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10867             if(a[i]){
10868                 delete El.cache[a[i].id || a[i]];
10869             }
10870         }
10871     };
10872
10873     // private
10874     // Garbage collection - uncache elements/purge listeners on orphaned elements
10875     // so we don't hold a reference and cause the browser to retain them
10876     El.garbageCollect = function(){
10877         if(!Roo.enableGarbageCollector){
10878             clearInterval(El.collectorThread);
10879             return;
10880         }
10881         for(var eid in El.cache){
10882             var el = El.cache[eid], d = el.dom;
10883             // -------------------------------------------------------
10884             // Determining what is garbage:
10885             // -------------------------------------------------------
10886             // !d
10887             // dom node is null, definitely garbage
10888             // -------------------------------------------------------
10889             // !d.parentNode
10890             // no parentNode == direct orphan, definitely garbage
10891             // -------------------------------------------------------
10892             // !d.offsetParent && !document.getElementById(eid)
10893             // display none elements have no offsetParent so we will
10894             // also try to look it up by it's id. However, check
10895             // offsetParent first so we don't do unneeded lookups.
10896             // This enables collection of elements that are not orphans
10897             // directly, but somewhere up the line they have an orphan
10898             // parent.
10899             // -------------------------------------------------------
10900             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10901                 delete El.cache[eid];
10902                 if(d && Roo.enableListenerCollection){
10903                     E.purgeElement(d);
10904                 }
10905             }
10906         }
10907     }
10908     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10909
10910
10911     // dom is optional
10912     El.Flyweight = function(dom){
10913         this.dom = dom;
10914     };
10915     El.Flyweight.prototype = El.prototype;
10916
10917     El._flyweights = {};
10918     /**
10919      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10920      * the dom node can be overwritten by other code.
10921      * @param {String/HTMLElement} el The dom node or id
10922      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10923      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10924      * @static
10925      * @return {Element} The shared Element object
10926      */
10927     El.fly = function(el, named){
10928         named = named || '_global';
10929         el = Roo.getDom(el);
10930         if(!el){
10931             return null;
10932         }
10933         if(!El._flyweights[named]){
10934             El._flyweights[named] = new El.Flyweight();
10935         }
10936         El._flyweights[named].dom = el;
10937         return El._flyweights[named];
10938     };
10939
10940     /**
10941      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10942      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10943      * Shorthand of {@link Roo.Element#get}
10944      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10945      * @return {Element} The Element object
10946      * @member Roo
10947      * @method get
10948      */
10949     Roo.get = El.get;
10950     /**
10951      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10952      * the dom node can be overwritten by other code.
10953      * Shorthand of {@link Roo.Element#fly}
10954      * @param {String/HTMLElement} el The dom node or id
10955      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10956      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10957      * @static
10958      * @return {Element} The shared Element object
10959      * @member Roo
10960      * @method fly
10961      */
10962     Roo.fly = El.fly;
10963
10964     // speedy lookup for elements never to box adjust
10965     var noBoxAdjust = Roo.isStrict ? {
10966         select:1
10967     } : {
10968         input:1, select:1, textarea:1
10969     };
10970     if(Roo.isIE || Roo.isGecko){
10971         noBoxAdjust['button'] = 1;
10972     }
10973
10974
10975     Roo.EventManager.on(window, 'unload', function(){
10976         delete El.cache;
10977         delete El._flyweights;
10978     });
10979 })();
10980
10981
10982
10983
10984 if(Roo.DomQuery){
10985     Roo.Element.selectorFunction = Roo.DomQuery.select;
10986 }
10987
10988 Roo.Element.select = function(selector, unique, root){
10989     var els;
10990     if(typeof selector == "string"){
10991         els = Roo.Element.selectorFunction(selector, root);
10992     }else if(selector.length !== undefined){
10993         els = selector;
10994     }else{
10995         throw "Invalid selector";
10996     }
10997     if(unique === true){
10998         return new Roo.CompositeElement(els);
10999     }else{
11000         return new Roo.CompositeElementLite(els);
11001     }
11002 };
11003 /**
11004  * Selects elements based on the passed CSS selector to enable working on them as 1.
11005  * @param {String/Array} selector The CSS selector or an array of elements
11006  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11007  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11008  * @return {CompositeElementLite/CompositeElement}
11009  * @member Roo
11010  * @method select
11011  */
11012 Roo.select = Roo.Element.select;
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027 /*
11028  * Based on:
11029  * Ext JS Library 1.1.1
11030  * Copyright(c) 2006-2007, Ext JS, LLC.
11031  *
11032  * Originally Released Under LGPL - original licence link has changed is not relivant.
11033  *
11034  * Fork - LGPL
11035  * <script type="text/javascript">
11036  */
11037
11038
11039
11040 //Notifies Element that fx methods are available
11041 Roo.enableFx = true;
11042
11043 /**
11044  * @class Roo.Fx
11045  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11046  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11047  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11048  * Element effects to work.</p><br/>
11049  *
11050  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11051  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11052  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11053  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11054  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11055  * expected results and should be done with care.</p><br/>
11056  *
11057  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11058  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11059 <pre>
11060 Value  Description
11061 -----  -----------------------------
11062 tl     The top left corner
11063 t      The center of the top edge
11064 tr     The top right corner
11065 l      The center of the left edge
11066 r      The center of the right edge
11067 bl     The bottom left corner
11068 b      The center of the bottom edge
11069 br     The bottom right corner
11070 </pre>
11071  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11072  * below are common options that can be passed to any Fx method.</b>
11073  * @cfg {Function} callback A function called when the effect is finished
11074  * @cfg {Object} scope The scope of the effect function
11075  * @cfg {String} easing A valid Easing value for the effect
11076  * @cfg {String} afterCls A css class to apply after the effect
11077  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11078  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11079  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11080  * effects that end with the element being visually hidden, ignored otherwise)
11081  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11082  * a function which returns such a specification that will be applied to the Element after the effect finishes
11083  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11084  * @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
11085  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11086  */
11087 Roo.Fx = {
11088         /**
11089          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11090          * origin for the slide effect.  This function automatically handles wrapping the element with
11091          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11092          * Usage:
11093          *<pre><code>
11094 // default: slide the element in from the top
11095 el.slideIn();
11096
11097 // custom: slide the element in from the right with a 2-second duration
11098 el.slideIn('r', { duration: 2 });
11099
11100 // common config options shown with default values
11101 el.slideIn('t', {
11102     easing: 'easeOut',
11103     duration: .5
11104 });
11105 </code></pre>
11106          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11107          * @param {Object} options (optional) Object literal with any of the Fx config options
11108          * @return {Roo.Element} The Element
11109          */
11110     slideIn : function(anchor, o){
11111         var el = this.getFxEl();
11112         o = o || {};
11113
11114         el.queueFx(o, function(){
11115
11116             anchor = anchor || "t";
11117
11118             // fix display to visibility
11119             this.fixDisplay();
11120
11121             // restore values after effect
11122             var r = this.getFxRestore();
11123             var b = this.getBox();
11124             // fixed size for slide
11125             this.setSize(b);
11126
11127             // wrap if needed
11128             var wrap = this.fxWrap(r.pos, o, "hidden");
11129
11130             var st = this.dom.style;
11131             st.visibility = "visible";
11132             st.position = "absolute";
11133
11134             // clear out temp styles after slide and unwrap
11135             var after = function(){
11136                 el.fxUnwrap(wrap, r.pos, o);
11137                 st.width = r.width;
11138                 st.height = r.height;
11139                 el.afterFx(o);
11140             };
11141             // time to calc the positions
11142             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11143
11144             switch(anchor.toLowerCase()){
11145                 case "t":
11146                     wrap.setSize(b.width, 0);
11147                     st.left = st.bottom = "0";
11148                     a = {height: bh};
11149                 break;
11150                 case "l":
11151                     wrap.setSize(0, b.height);
11152                     st.right = st.top = "0";
11153                     a = {width: bw};
11154                 break;
11155                 case "r":
11156                     wrap.setSize(0, b.height);
11157                     wrap.setX(b.right);
11158                     st.left = st.top = "0";
11159                     a = {width: bw, points: pt};
11160                 break;
11161                 case "b":
11162                     wrap.setSize(b.width, 0);
11163                     wrap.setY(b.bottom);
11164                     st.left = st.top = "0";
11165                     a = {height: bh, points: pt};
11166                 break;
11167                 case "tl":
11168                     wrap.setSize(0, 0);
11169                     st.right = st.bottom = "0";
11170                     a = {width: bw, height: bh};
11171                 break;
11172                 case "bl":
11173                     wrap.setSize(0, 0);
11174                     wrap.setY(b.y+b.height);
11175                     st.right = st.top = "0";
11176                     a = {width: bw, height: bh, points: pt};
11177                 break;
11178                 case "br":
11179                     wrap.setSize(0, 0);
11180                     wrap.setXY([b.right, b.bottom]);
11181                     st.left = st.top = "0";
11182                     a = {width: bw, height: bh, points: pt};
11183                 break;
11184                 case "tr":
11185                     wrap.setSize(0, 0);
11186                     wrap.setX(b.x+b.width);
11187                     st.left = st.bottom = "0";
11188                     a = {width: bw, height: bh, points: pt};
11189                 break;
11190             }
11191             this.dom.style.visibility = "visible";
11192             wrap.show();
11193
11194             arguments.callee.anim = wrap.fxanim(a,
11195                 o,
11196                 'motion',
11197                 .5,
11198                 'easeOut', after);
11199         });
11200         return this;
11201     },
11202     
11203         /**
11204          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11205          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11206          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11207          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11208          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11209          * Usage:
11210          *<pre><code>
11211 // default: slide the element out to the top
11212 el.slideOut();
11213
11214 // custom: slide the element out to the right with a 2-second duration
11215 el.slideOut('r', { duration: 2 });
11216
11217 // common config options shown with default values
11218 el.slideOut('t', {
11219     easing: 'easeOut',
11220     duration: .5,
11221     remove: false,
11222     useDisplay: false
11223 });
11224 </code></pre>
11225          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11226          * @param {Object} options (optional) Object literal with any of the Fx config options
11227          * @return {Roo.Element} The Element
11228          */
11229     slideOut : function(anchor, o){
11230         var el = this.getFxEl();
11231         o = o || {};
11232
11233         el.queueFx(o, function(){
11234
11235             anchor = anchor || "t";
11236
11237             // restore values after effect
11238             var r = this.getFxRestore();
11239             
11240             var b = this.getBox();
11241             // fixed size for slide
11242             this.setSize(b);
11243
11244             // wrap if needed
11245             var wrap = this.fxWrap(r.pos, o, "visible");
11246
11247             var st = this.dom.style;
11248             st.visibility = "visible";
11249             st.position = "absolute";
11250
11251             wrap.setSize(b);
11252
11253             var after = function(){
11254                 if(o.useDisplay){
11255                     el.setDisplayed(false);
11256                 }else{
11257                     el.hide();
11258                 }
11259
11260                 el.fxUnwrap(wrap, r.pos, o);
11261
11262                 st.width = r.width;
11263                 st.height = r.height;
11264
11265                 el.afterFx(o);
11266             };
11267
11268             var a, zero = {to: 0};
11269             switch(anchor.toLowerCase()){
11270                 case "t":
11271                     st.left = st.bottom = "0";
11272                     a = {height: zero};
11273                 break;
11274                 case "l":
11275                     st.right = st.top = "0";
11276                     a = {width: zero};
11277                 break;
11278                 case "r":
11279                     st.left = st.top = "0";
11280                     a = {width: zero, points: {to:[b.right, b.y]}};
11281                 break;
11282                 case "b":
11283                     st.left = st.top = "0";
11284                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11285                 break;
11286                 case "tl":
11287                     st.right = st.bottom = "0";
11288                     a = {width: zero, height: zero};
11289                 break;
11290                 case "bl":
11291                     st.right = st.top = "0";
11292                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11293                 break;
11294                 case "br":
11295                     st.left = st.top = "0";
11296                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11297                 break;
11298                 case "tr":
11299                     st.left = st.bottom = "0";
11300                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11301                 break;
11302             }
11303
11304             arguments.callee.anim = wrap.fxanim(a,
11305                 o,
11306                 'motion',
11307                 .5,
11308                 "easeOut", after);
11309         });
11310         return this;
11311     },
11312
11313         /**
11314          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11315          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11316          * The element must be removed from the DOM using the 'remove' config option if desired.
11317          * Usage:
11318          *<pre><code>
11319 // default
11320 el.puff();
11321
11322 // common config options shown with default values
11323 el.puff({
11324     easing: 'easeOut',
11325     duration: .5,
11326     remove: false,
11327     useDisplay: false
11328 });
11329 </code></pre>
11330          * @param {Object} options (optional) Object literal with any of the Fx config options
11331          * @return {Roo.Element} The Element
11332          */
11333     puff : function(o){
11334         var el = this.getFxEl();
11335         o = o || {};
11336
11337         el.queueFx(o, function(){
11338             this.clearOpacity();
11339             this.show();
11340
11341             // restore values after effect
11342             var r = this.getFxRestore();
11343             var st = this.dom.style;
11344
11345             var after = function(){
11346                 if(o.useDisplay){
11347                     el.setDisplayed(false);
11348                 }else{
11349                     el.hide();
11350                 }
11351
11352                 el.clearOpacity();
11353
11354                 el.setPositioning(r.pos);
11355                 st.width = r.width;
11356                 st.height = r.height;
11357                 st.fontSize = '';
11358                 el.afterFx(o);
11359             };
11360
11361             var width = this.getWidth();
11362             var height = this.getHeight();
11363
11364             arguments.callee.anim = this.fxanim({
11365                     width : {to: this.adjustWidth(width * 2)},
11366                     height : {to: this.adjustHeight(height * 2)},
11367                     points : {by: [-(width * .5), -(height * .5)]},
11368                     opacity : {to: 0},
11369                     fontSize: {to:200, unit: "%"}
11370                 },
11371                 o,
11372                 'motion',
11373                 .5,
11374                 "easeOut", after);
11375         });
11376         return this;
11377     },
11378
11379         /**
11380          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11381          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11382          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11383          * Usage:
11384          *<pre><code>
11385 // default
11386 el.switchOff();
11387
11388 // all config options shown with default values
11389 el.switchOff({
11390     easing: 'easeIn',
11391     duration: .3,
11392     remove: false,
11393     useDisplay: false
11394 });
11395 </code></pre>
11396          * @param {Object} options (optional) Object literal with any of the Fx config options
11397          * @return {Roo.Element} The Element
11398          */
11399     switchOff : function(o){
11400         var el = this.getFxEl();
11401         o = o || {};
11402
11403         el.queueFx(o, function(){
11404             this.clearOpacity();
11405             this.clip();
11406
11407             // restore values after effect
11408             var r = this.getFxRestore();
11409             var st = this.dom.style;
11410
11411             var after = function(){
11412                 if(o.useDisplay){
11413                     el.setDisplayed(false);
11414                 }else{
11415                     el.hide();
11416                 }
11417
11418                 el.clearOpacity();
11419                 el.setPositioning(r.pos);
11420                 st.width = r.width;
11421                 st.height = r.height;
11422
11423                 el.afterFx(o);
11424             };
11425
11426             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11427                 this.clearOpacity();
11428                 (function(){
11429                     this.fxanim({
11430                         height:{to:1},
11431                         points:{by:[0, this.getHeight() * .5]}
11432                     }, o, 'motion', 0.3, 'easeIn', after);
11433                 }).defer(100, this);
11434             });
11435         });
11436         return this;
11437     },
11438
11439     /**
11440      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11441      * changed using the "attr" config option) and then fading back to the original color. If no original
11442      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11443      * Usage:
11444 <pre><code>
11445 // default: highlight background to yellow
11446 el.highlight();
11447
11448 // custom: highlight foreground text to blue for 2 seconds
11449 el.highlight("0000ff", { attr: 'color', duration: 2 });
11450
11451 // common config options shown with default values
11452 el.highlight("ffff9c", {
11453     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11454     endColor: (current color) or "ffffff",
11455     easing: 'easeIn',
11456     duration: 1
11457 });
11458 </code></pre>
11459      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11460      * @param {Object} options (optional) Object literal with any of the Fx config options
11461      * @return {Roo.Element} The Element
11462      */ 
11463     highlight : function(color, o){
11464         var el = this.getFxEl();
11465         o = o || {};
11466
11467         el.queueFx(o, function(){
11468             color = color || "ffff9c";
11469             attr = o.attr || "backgroundColor";
11470
11471             this.clearOpacity();
11472             this.show();
11473
11474             var origColor = this.getColor(attr);
11475             var restoreColor = this.dom.style[attr];
11476             endColor = (o.endColor || origColor) || "ffffff";
11477
11478             var after = function(){
11479                 el.dom.style[attr] = restoreColor;
11480                 el.afterFx(o);
11481             };
11482
11483             var a = {};
11484             a[attr] = {from: color, to: endColor};
11485             arguments.callee.anim = this.fxanim(a,
11486                 o,
11487                 'color',
11488                 1,
11489                 'easeIn', after);
11490         });
11491         return this;
11492     },
11493
11494    /**
11495     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11496     * Usage:
11497 <pre><code>
11498 // default: a single light blue ripple
11499 el.frame();
11500
11501 // custom: 3 red ripples lasting 3 seconds total
11502 el.frame("ff0000", 3, { duration: 3 });
11503
11504 // common config options shown with default values
11505 el.frame("C3DAF9", 1, {
11506     duration: 1 //duration of entire animation (not each individual ripple)
11507     // Note: Easing is not configurable and will be ignored if included
11508 });
11509 </code></pre>
11510     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11511     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11512     * @param {Object} options (optional) Object literal with any of the Fx config options
11513     * @return {Roo.Element} The Element
11514     */
11515     frame : function(color, count, o){
11516         var el = this.getFxEl();
11517         o = o || {};
11518
11519         el.queueFx(o, function(){
11520             color = color || "#C3DAF9";
11521             if(color.length == 6){
11522                 color = "#" + color;
11523             }
11524             count = count || 1;
11525             duration = o.duration || 1;
11526             this.show();
11527
11528             var b = this.getBox();
11529             var animFn = function(){
11530                 var proxy = this.createProxy({
11531
11532                      style:{
11533                         visbility:"hidden",
11534                         position:"absolute",
11535                         "z-index":"35000", // yee haw
11536                         border:"0px solid " + color
11537                      }
11538                   });
11539                 var scale = Roo.isBorderBox ? 2 : 1;
11540                 proxy.animate({
11541                     top:{from:b.y, to:b.y - 20},
11542                     left:{from:b.x, to:b.x - 20},
11543                     borderWidth:{from:0, to:10},
11544                     opacity:{from:1, to:0},
11545                     height:{from:b.height, to:(b.height + (20*scale))},
11546                     width:{from:b.width, to:(b.width + (20*scale))}
11547                 }, duration, function(){
11548                     proxy.remove();
11549                 });
11550                 if(--count > 0){
11551                      animFn.defer((duration/2)*1000, this);
11552                 }else{
11553                     el.afterFx(o);
11554                 }
11555             };
11556             animFn.call(this);
11557         });
11558         return this;
11559     },
11560
11561    /**
11562     * Creates a pause before any subsequent queued effects begin.  If there are
11563     * no effects queued after the pause it will have no effect.
11564     * Usage:
11565 <pre><code>
11566 el.pause(1);
11567 </code></pre>
11568     * @param {Number} seconds The length of time to pause (in seconds)
11569     * @return {Roo.Element} The Element
11570     */
11571     pause : function(seconds){
11572         var el = this.getFxEl();
11573         var o = {};
11574
11575         el.queueFx(o, function(){
11576             setTimeout(function(){
11577                 el.afterFx(o);
11578             }, seconds * 1000);
11579         });
11580         return this;
11581     },
11582
11583    /**
11584     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11585     * using the "endOpacity" config option.
11586     * Usage:
11587 <pre><code>
11588 // default: fade in from opacity 0 to 100%
11589 el.fadeIn();
11590
11591 // custom: fade in from opacity 0 to 75% over 2 seconds
11592 el.fadeIn({ endOpacity: .75, duration: 2});
11593
11594 // common config options shown with default values
11595 el.fadeIn({
11596     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11597     easing: 'easeOut',
11598     duration: .5
11599 });
11600 </code></pre>
11601     * @param {Object} options (optional) Object literal with any of the Fx config options
11602     * @return {Roo.Element} The Element
11603     */
11604     fadeIn : function(o){
11605         var el = this.getFxEl();
11606         o = o || {};
11607         el.queueFx(o, function(){
11608             this.setOpacity(0);
11609             this.fixDisplay();
11610             this.dom.style.visibility = 'visible';
11611             var to = o.endOpacity || 1;
11612             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11613                 o, null, .5, "easeOut", function(){
11614                 if(to == 1){
11615                     this.clearOpacity();
11616                 }
11617                 el.afterFx(o);
11618             });
11619         });
11620         return this;
11621     },
11622
11623    /**
11624     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11625     * using the "endOpacity" config option.
11626     * Usage:
11627 <pre><code>
11628 // default: fade out from the element's current opacity to 0
11629 el.fadeOut();
11630
11631 // custom: fade out from the element's current opacity to 25% over 2 seconds
11632 el.fadeOut({ endOpacity: .25, duration: 2});
11633
11634 // common config options shown with default values
11635 el.fadeOut({
11636     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11637     easing: 'easeOut',
11638     duration: .5
11639     remove: false,
11640     useDisplay: false
11641 });
11642 </code></pre>
11643     * @param {Object} options (optional) Object literal with any of the Fx config options
11644     * @return {Roo.Element} The Element
11645     */
11646     fadeOut : function(o){
11647         var el = this.getFxEl();
11648         o = o || {};
11649         el.queueFx(o, function(){
11650             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11651                 o, null, .5, "easeOut", function(){
11652                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11653                      this.dom.style.display = "none";
11654                 }else{
11655                      this.dom.style.visibility = "hidden";
11656                 }
11657                 this.clearOpacity();
11658                 el.afterFx(o);
11659             });
11660         });
11661         return this;
11662     },
11663
11664    /**
11665     * Animates the transition of an element's dimensions from a starting height/width
11666     * to an ending height/width.
11667     * Usage:
11668 <pre><code>
11669 // change height and width to 100x100 pixels
11670 el.scale(100, 100);
11671
11672 // common config options shown with default values.  The height and width will default to
11673 // the element's existing values if passed as null.
11674 el.scale(
11675     [element's width],
11676     [element's height], {
11677     easing: 'easeOut',
11678     duration: .35
11679 });
11680 </code></pre>
11681     * @param {Number} width  The new width (pass undefined to keep the original width)
11682     * @param {Number} height  The new height (pass undefined to keep the original height)
11683     * @param {Object} options (optional) Object literal with any of the Fx config options
11684     * @return {Roo.Element} The Element
11685     */
11686     scale : function(w, h, o){
11687         this.shift(Roo.apply({}, o, {
11688             width: w,
11689             height: h
11690         }));
11691         return this;
11692     },
11693
11694    /**
11695     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11696     * Any of these properties not specified in the config object will not be changed.  This effect 
11697     * requires that at least one new dimension, position or opacity setting must be passed in on
11698     * the config object in order for the function to have any effect.
11699     * Usage:
11700 <pre><code>
11701 // slide the element horizontally to x position 200 while changing the height and opacity
11702 el.shift({ x: 200, height: 50, opacity: .8 });
11703
11704 // common config options shown with default values.
11705 el.shift({
11706     width: [element's width],
11707     height: [element's height],
11708     x: [element's x position],
11709     y: [element's y position],
11710     opacity: [element's opacity],
11711     easing: 'easeOut',
11712     duration: .35
11713 });
11714 </code></pre>
11715     * @param {Object} options  Object literal with any of the Fx config options
11716     * @return {Roo.Element} The Element
11717     */
11718     shift : function(o){
11719         var el = this.getFxEl();
11720         o = o || {};
11721         el.queueFx(o, function(){
11722             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11723             if(w !== undefined){
11724                 a.width = {to: this.adjustWidth(w)};
11725             }
11726             if(h !== undefined){
11727                 a.height = {to: this.adjustHeight(h)};
11728             }
11729             if(x !== undefined || y !== undefined){
11730                 a.points = {to: [
11731                     x !== undefined ? x : this.getX(),
11732                     y !== undefined ? y : this.getY()
11733                 ]};
11734             }
11735             if(op !== undefined){
11736                 a.opacity = {to: op};
11737             }
11738             if(o.xy !== undefined){
11739                 a.points = {to: o.xy};
11740             }
11741             arguments.callee.anim = this.fxanim(a,
11742                 o, 'motion', .35, "easeOut", function(){
11743                 el.afterFx(o);
11744             });
11745         });
11746         return this;
11747     },
11748
11749         /**
11750          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11751          * ending point of the effect.
11752          * Usage:
11753          *<pre><code>
11754 // default: slide the element downward while fading out
11755 el.ghost();
11756
11757 // custom: slide the element out to the right with a 2-second duration
11758 el.ghost('r', { duration: 2 });
11759
11760 // common config options shown with default values
11761 el.ghost('b', {
11762     easing: 'easeOut',
11763     duration: .5
11764     remove: false,
11765     useDisplay: false
11766 });
11767 </code></pre>
11768          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11769          * @param {Object} options (optional) Object literal with any of the Fx config options
11770          * @return {Roo.Element} The Element
11771          */
11772     ghost : function(anchor, o){
11773         var el = this.getFxEl();
11774         o = o || {};
11775
11776         el.queueFx(o, function(){
11777             anchor = anchor || "b";
11778
11779             // restore values after effect
11780             var r = this.getFxRestore();
11781             var w = this.getWidth(),
11782                 h = this.getHeight();
11783
11784             var st = this.dom.style;
11785
11786             var after = function(){
11787                 if(o.useDisplay){
11788                     el.setDisplayed(false);
11789                 }else{
11790                     el.hide();
11791                 }
11792
11793                 el.clearOpacity();
11794                 el.setPositioning(r.pos);
11795                 st.width = r.width;
11796                 st.height = r.height;
11797
11798                 el.afterFx(o);
11799             };
11800
11801             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11802             switch(anchor.toLowerCase()){
11803                 case "t":
11804                     pt.by = [0, -h];
11805                 break;
11806                 case "l":
11807                     pt.by = [-w, 0];
11808                 break;
11809                 case "r":
11810                     pt.by = [w, 0];
11811                 break;
11812                 case "b":
11813                     pt.by = [0, h];
11814                 break;
11815                 case "tl":
11816                     pt.by = [-w, -h];
11817                 break;
11818                 case "bl":
11819                     pt.by = [-w, h];
11820                 break;
11821                 case "br":
11822                     pt.by = [w, h];
11823                 break;
11824                 case "tr":
11825                     pt.by = [w, -h];
11826                 break;
11827             }
11828
11829             arguments.callee.anim = this.fxanim(a,
11830                 o,
11831                 'motion',
11832                 .5,
11833                 "easeOut", after);
11834         });
11835         return this;
11836     },
11837
11838         /**
11839          * Ensures that all effects queued after syncFx is called on the element are
11840          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11841          * @return {Roo.Element} The Element
11842          */
11843     syncFx : function(){
11844         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11845             block : false,
11846             concurrent : true,
11847             stopFx : false
11848         });
11849         return this;
11850     },
11851
11852         /**
11853          * Ensures that all effects queued after sequenceFx is called on the element are
11854          * run in sequence.  This is the opposite of {@link #syncFx}.
11855          * @return {Roo.Element} The Element
11856          */
11857     sequenceFx : function(){
11858         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11859             block : false,
11860             concurrent : false,
11861             stopFx : false
11862         });
11863         return this;
11864     },
11865
11866         /* @private */
11867     nextFx : function(){
11868         var ef = this.fxQueue[0];
11869         if(ef){
11870             ef.call(this);
11871         }
11872     },
11873
11874         /**
11875          * Returns true if the element has any effects actively running or queued, else returns false.
11876          * @return {Boolean} True if element has active effects, else false
11877          */
11878     hasActiveFx : function(){
11879         return this.fxQueue && this.fxQueue[0];
11880     },
11881
11882         /**
11883          * Stops any running effects and clears the element's internal effects queue if it contains
11884          * any additional effects that haven't started yet.
11885          * @return {Roo.Element} The Element
11886          */
11887     stopFx : function(){
11888         if(this.hasActiveFx()){
11889             var cur = this.fxQueue[0];
11890             if(cur && cur.anim && cur.anim.isAnimated()){
11891                 this.fxQueue = [cur]; // clear out others
11892                 cur.anim.stop(true);
11893             }
11894         }
11895         return this;
11896     },
11897
11898         /* @private */
11899     beforeFx : function(o){
11900         if(this.hasActiveFx() && !o.concurrent){
11901            if(o.stopFx){
11902                this.stopFx();
11903                return true;
11904            }
11905            return false;
11906         }
11907         return true;
11908     },
11909
11910         /**
11911          * Returns true if the element is currently blocking so that no other effect can be queued
11912          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11913          * used to ensure that an effect initiated by a user action runs to completion prior to the
11914          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11915          * @return {Boolean} True if blocking, else false
11916          */
11917     hasFxBlock : function(){
11918         var q = this.fxQueue;
11919         return q && q[0] && q[0].block;
11920     },
11921
11922         /* @private */
11923     queueFx : function(o, fn){
11924         if(!this.fxQueue){
11925             this.fxQueue = [];
11926         }
11927         if(!this.hasFxBlock()){
11928             Roo.applyIf(o, this.fxDefaults);
11929             if(!o.concurrent){
11930                 var run = this.beforeFx(o);
11931                 fn.block = o.block;
11932                 this.fxQueue.push(fn);
11933                 if(run){
11934                     this.nextFx();
11935                 }
11936             }else{
11937                 fn.call(this);
11938             }
11939         }
11940         return this;
11941     },
11942
11943         /* @private */
11944     fxWrap : function(pos, o, vis){
11945         var wrap;
11946         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11947             var wrapXY;
11948             if(o.fixPosition){
11949                 wrapXY = this.getXY();
11950             }
11951             var div = document.createElement("div");
11952             div.style.visibility = vis;
11953             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11954             wrap.setPositioning(pos);
11955             if(wrap.getStyle("position") == "static"){
11956                 wrap.position("relative");
11957             }
11958             this.clearPositioning('auto');
11959             wrap.clip();
11960             wrap.dom.appendChild(this.dom);
11961             if(wrapXY){
11962                 wrap.setXY(wrapXY);
11963             }
11964         }
11965         return wrap;
11966     },
11967
11968         /* @private */
11969     fxUnwrap : function(wrap, pos, o){
11970         this.clearPositioning();
11971         this.setPositioning(pos);
11972         if(!o.wrap){
11973             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11974             wrap.remove();
11975         }
11976     },
11977
11978         /* @private */
11979     getFxRestore : function(){
11980         var st = this.dom.style;
11981         return {pos: this.getPositioning(), width: st.width, height : st.height};
11982     },
11983
11984         /* @private */
11985     afterFx : function(o){
11986         if(o.afterStyle){
11987             this.applyStyles(o.afterStyle);
11988         }
11989         if(o.afterCls){
11990             this.addClass(o.afterCls);
11991         }
11992         if(o.remove === true){
11993             this.remove();
11994         }
11995         Roo.callback(o.callback, o.scope, [this]);
11996         if(!o.concurrent){
11997             this.fxQueue.shift();
11998             this.nextFx();
11999         }
12000     },
12001
12002         /* @private */
12003     getFxEl : function(){ // support for composite element fx
12004         return Roo.get(this.dom);
12005     },
12006
12007         /* @private */
12008     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12009         animType = animType || 'run';
12010         opt = opt || {};
12011         var anim = Roo.lib.Anim[animType](
12012             this.dom, args,
12013             (opt.duration || defaultDur) || .35,
12014             (opt.easing || defaultEase) || 'easeOut',
12015             function(){
12016                 Roo.callback(cb, this);
12017             },
12018             this
12019         );
12020         opt.anim = anim;
12021         return anim;
12022     }
12023 };
12024
12025 // backwords compat
12026 Roo.Fx.resize = Roo.Fx.scale;
12027
12028 //When included, Roo.Fx is automatically applied to Element so that all basic
12029 //effects are available directly via the Element API
12030 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12031  * Based on:
12032  * Ext JS Library 1.1.1
12033  * Copyright(c) 2006-2007, Ext JS, LLC.
12034  *
12035  * Originally Released Under LGPL - original licence link has changed is not relivant.
12036  *
12037  * Fork - LGPL
12038  * <script type="text/javascript">
12039  */
12040
12041
12042 /**
12043  * @class Roo.CompositeElement
12044  * Standard composite class. Creates a Roo.Element for every element in the collection.
12045  * <br><br>
12046  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12047  * actions will be performed on all the elements in this collection.</b>
12048  * <br><br>
12049  * All methods return <i>this</i> and can be chained.
12050  <pre><code>
12051  var els = Roo.select("#some-el div.some-class", true);
12052  // or select directly from an existing element
12053  var el = Roo.get('some-el');
12054  el.select('div.some-class', true);
12055
12056  els.setWidth(100); // all elements become 100 width
12057  els.hide(true); // all elements fade out and hide
12058  // or
12059  els.setWidth(100).hide(true);
12060  </code></pre>
12061  */
12062 Roo.CompositeElement = function(els){
12063     this.elements = [];
12064     this.addElements(els);
12065 };
12066 Roo.CompositeElement.prototype = {
12067     isComposite: true,
12068     addElements : function(els){
12069         if(!els) {
12070             return this;
12071         }
12072         if(typeof els == "string"){
12073             els = Roo.Element.selectorFunction(els);
12074         }
12075         var yels = this.elements;
12076         var index = yels.length-1;
12077         for(var i = 0, len = els.length; i < len; i++) {
12078                 yels[++index] = Roo.get(els[i]);
12079         }
12080         return this;
12081     },
12082
12083     /**
12084     * Clears this composite and adds the elements returned by the passed selector.
12085     * @param {String/Array} els A string CSS selector, an array of elements or an element
12086     * @return {CompositeElement} this
12087     */
12088     fill : function(els){
12089         this.elements = [];
12090         this.add(els);
12091         return this;
12092     },
12093
12094     /**
12095     * Filters this composite to only elements that match the passed selector.
12096     * @param {String} selector A string CSS selector
12097     * @param {Boolean} inverse return inverse filter (not matches)
12098     * @return {CompositeElement} this
12099     */
12100     filter : function(selector, inverse){
12101         var els = [];
12102         inverse = inverse || false;
12103         this.each(function(el){
12104             var match = inverse ? !el.is(selector) : el.is(selector);
12105             if(match){
12106                 els[els.length] = el.dom;
12107             }
12108         });
12109         this.fill(els);
12110         return this;
12111     },
12112
12113     invoke : function(fn, args){
12114         var els = this.elements;
12115         for(var i = 0, len = els.length; i < len; i++) {
12116                 Roo.Element.prototype[fn].apply(els[i], args);
12117         }
12118         return this;
12119     },
12120     /**
12121     * Adds elements to this composite.
12122     * @param {String/Array} els A string CSS selector, an array of elements or an element
12123     * @return {CompositeElement} this
12124     */
12125     add : function(els){
12126         if(typeof els == "string"){
12127             this.addElements(Roo.Element.selectorFunction(els));
12128         }else if(els.length !== undefined){
12129             this.addElements(els);
12130         }else{
12131             this.addElements([els]);
12132         }
12133         return this;
12134     },
12135     /**
12136     * Calls the passed function passing (el, this, index) for each element in this composite.
12137     * @param {Function} fn The function to call
12138     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12139     * @return {CompositeElement} this
12140     */
12141     each : function(fn, scope){
12142         var els = this.elements;
12143         for(var i = 0, len = els.length; i < len; i++){
12144             if(fn.call(scope || els[i], els[i], this, i) === false) {
12145                 break;
12146             }
12147         }
12148         return this;
12149     },
12150
12151     /**
12152      * Returns the Element object at the specified index
12153      * @param {Number} index
12154      * @return {Roo.Element}
12155      */
12156     item : function(index){
12157         return this.elements[index] || null;
12158     },
12159
12160     /**
12161      * Returns the first Element
12162      * @return {Roo.Element}
12163      */
12164     first : function(){
12165         return this.item(0);
12166     },
12167
12168     /**
12169      * Returns the last Element
12170      * @return {Roo.Element}
12171      */
12172     last : function(){
12173         return this.item(this.elements.length-1);
12174     },
12175
12176     /**
12177      * Returns the number of elements in this composite
12178      * @return Number
12179      */
12180     getCount : function(){
12181         return this.elements.length;
12182     },
12183
12184     /**
12185      * Returns true if this composite contains the passed element
12186      * @return Boolean
12187      */
12188     contains : function(el){
12189         return this.indexOf(el) !== -1;
12190     },
12191
12192     /**
12193      * Returns true if this composite contains the passed element
12194      * @return Boolean
12195      */
12196     indexOf : function(el){
12197         return this.elements.indexOf(Roo.get(el));
12198     },
12199
12200
12201     /**
12202     * Removes the specified element(s).
12203     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12204     * or an array of any of those.
12205     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12206     * @return {CompositeElement} this
12207     */
12208     removeElement : function(el, removeDom){
12209         if(el instanceof Array){
12210             for(var i = 0, len = el.length; i < len; i++){
12211                 this.removeElement(el[i]);
12212             }
12213             return this;
12214         }
12215         var index = typeof el == 'number' ? el : this.indexOf(el);
12216         if(index !== -1){
12217             if(removeDom){
12218                 var d = this.elements[index];
12219                 if(d.dom){
12220                     d.remove();
12221                 }else{
12222                     d.parentNode.removeChild(d);
12223                 }
12224             }
12225             this.elements.splice(index, 1);
12226         }
12227         return this;
12228     },
12229
12230     /**
12231     * Replaces the specified element with the passed element.
12232     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12233     * to replace.
12234     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12235     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12236     * @return {CompositeElement} this
12237     */
12238     replaceElement : function(el, replacement, domReplace){
12239         var index = typeof el == 'number' ? el : this.indexOf(el);
12240         if(index !== -1){
12241             if(domReplace){
12242                 this.elements[index].replaceWith(replacement);
12243             }else{
12244                 this.elements.splice(index, 1, Roo.get(replacement))
12245             }
12246         }
12247         return this;
12248     },
12249
12250     /**
12251      * Removes all elements.
12252      */
12253     clear : function(){
12254         this.elements = [];
12255     }
12256 };
12257 (function(){
12258     Roo.CompositeElement.createCall = function(proto, fnName){
12259         if(!proto[fnName]){
12260             proto[fnName] = function(){
12261                 return this.invoke(fnName, arguments);
12262             };
12263         }
12264     };
12265     for(var fnName in Roo.Element.prototype){
12266         if(typeof Roo.Element.prototype[fnName] == "function"){
12267             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12268         }
12269     };
12270 })();
12271 /*
12272  * Based on:
12273  * Ext JS Library 1.1.1
12274  * Copyright(c) 2006-2007, Ext JS, LLC.
12275  *
12276  * Originally Released Under LGPL - original licence link has changed is not relivant.
12277  *
12278  * Fork - LGPL
12279  * <script type="text/javascript">
12280  */
12281
12282 /**
12283  * @class Roo.CompositeElementLite
12284  * @extends Roo.CompositeElement
12285  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12286  <pre><code>
12287  var els = Roo.select("#some-el div.some-class");
12288  // or select directly from an existing element
12289  var el = Roo.get('some-el');
12290  el.select('div.some-class');
12291
12292  els.setWidth(100); // all elements become 100 width
12293  els.hide(true); // all elements fade out and hide
12294  // or
12295  els.setWidth(100).hide(true);
12296  </code></pre><br><br>
12297  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12298  * actions will be performed on all the elements in this collection.</b>
12299  */
12300 Roo.CompositeElementLite = function(els){
12301     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12302     this.el = new Roo.Element.Flyweight();
12303 };
12304 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12305     addElements : function(els){
12306         if(els){
12307             if(els instanceof Array){
12308                 this.elements = this.elements.concat(els);
12309             }else{
12310                 var yels = this.elements;
12311                 var index = yels.length-1;
12312                 for(var i = 0, len = els.length; i < len; i++) {
12313                     yels[++index] = els[i];
12314                 }
12315             }
12316         }
12317         return this;
12318     },
12319     invoke : function(fn, args){
12320         var els = this.elements;
12321         var el = this.el;
12322         for(var i = 0, len = els.length; i < len; i++) {
12323             el.dom = els[i];
12324                 Roo.Element.prototype[fn].apply(el, args);
12325         }
12326         return this;
12327     },
12328     /**
12329      * Returns a flyweight Element of the dom element object at the specified index
12330      * @param {Number} index
12331      * @return {Roo.Element}
12332      */
12333     item : function(index){
12334         if(!this.elements[index]){
12335             return null;
12336         }
12337         this.el.dom = this.elements[index];
12338         return this.el;
12339     },
12340
12341     // fixes scope with flyweight
12342     addListener : function(eventName, handler, scope, opt){
12343         var els = this.elements;
12344         for(var i = 0, len = els.length; i < len; i++) {
12345             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12346         }
12347         return this;
12348     },
12349
12350     /**
12351     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12352     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12353     * a reference to the dom node, use el.dom.</b>
12354     * @param {Function} fn The function to call
12355     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12356     * @return {CompositeElement} this
12357     */
12358     each : function(fn, scope){
12359         var els = this.elements;
12360         var el = this.el;
12361         for(var i = 0, len = els.length; i < len; i++){
12362             el.dom = els[i];
12363                 if(fn.call(scope || el, el, this, i) === false){
12364                 break;
12365             }
12366         }
12367         return this;
12368     },
12369
12370     indexOf : function(el){
12371         return this.elements.indexOf(Roo.getDom(el));
12372     },
12373
12374     replaceElement : function(el, replacement, domReplace){
12375         var index = typeof el == 'number' ? el : this.indexOf(el);
12376         if(index !== -1){
12377             replacement = Roo.getDom(replacement);
12378             if(domReplace){
12379                 var d = this.elements[index];
12380                 d.parentNode.insertBefore(replacement, d);
12381                 d.parentNode.removeChild(d);
12382             }
12383             this.elements.splice(index, 1, replacement);
12384         }
12385         return this;
12386     }
12387 });
12388 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12389
12390 /*
12391  * Based on:
12392  * Ext JS Library 1.1.1
12393  * Copyright(c) 2006-2007, Ext JS, LLC.
12394  *
12395  * Originally Released Under LGPL - original licence link has changed is not relivant.
12396  *
12397  * Fork - LGPL
12398  * <script type="text/javascript">
12399  */
12400
12401  
12402
12403 /**
12404  * @class Roo.data.Connection
12405  * @extends Roo.util.Observable
12406  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12407  * either to a configured URL, or to a URL specified at request time. 
12408  * 
12409  * Requests made by this class are asynchronous, and will return immediately. No data from
12410  * the server will be available to the statement immediately following the {@link #request} call.
12411  * To process returned data, use a callback in the request options object, or an event listener.
12412  * 
12413  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12414  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12415  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12416  * property and, if present, the IFRAME's XML document as the responseXML property.
12417  * 
12418  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12419  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12420  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12421  * standard DOM methods.
12422  * @constructor
12423  * @param {Object} config a configuration object.
12424  */
12425 Roo.data.Connection = function(config){
12426     Roo.apply(this, config);
12427     this.addEvents({
12428         /**
12429          * @event beforerequest
12430          * Fires before a network request is made to retrieve a data object.
12431          * @param {Connection} conn This Connection object.
12432          * @param {Object} options The options config object passed to the {@link #request} method.
12433          */
12434         "beforerequest" : true,
12435         /**
12436          * @event requestcomplete
12437          * Fires if the request was successfully completed.
12438          * @param {Connection} conn This Connection object.
12439          * @param {Object} response The XHR object containing the response data.
12440          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12441          * @param {Object} options The options config object passed to the {@link #request} method.
12442          */
12443         "requestcomplete" : true,
12444         /**
12445          * @event requestexception
12446          * Fires if an error HTTP status was returned from the server.
12447          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12448          * @param {Connection} conn This Connection object.
12449          * @param {Object} response The XHR object containing the response data.
12450          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12451          * @param {Object} options The options config object passed to the {@link #request} method.
12452          */
12453         "requestexception" : true
12454     });
12455     Roo.data.Connection.superclass.constructor.call(this);
12456 };
12457
12458 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12459     /**
12460      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12461      */
12462     /**
12463      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12464      * extra parameters to each request made by this object. (defaults to undefined)
12465      */
12466     /**
12467      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12468      *  to each request made by this object. (defaults to undefined)
12469      */
12470     /**
12471      * @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)
12472      */
12473     /**
12474      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12475      */
12476     timeout : 30000,
12477     /**
12478      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12479      * @type Boolean
12480      */
12481     autoAbort:false,
12482
12483     /**
12484      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12485      * @type Boolean
12486      */
12487     disableCaching: true,
12488
12489     /**
12490      * Sends an HTTP request to a remote server.
12491      * @param {Object} options An object which may contain the following properties:<ul>
12492      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12493      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12494      * request, a url encoded string or a function to call to get either.</li>
12495      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12496      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12497      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12498      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12499      * <li>options {Object} The parameter to the request call.</li>
12500      * <li>success {Boolean} True if the request succeeded.</li>
12501      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12502      * </ul></li>
12503      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12504      * The callback is passed the following parameters:<ul>
12505      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12506      * <li>options {Object} The parameter to the request call.</li>
12507      * </ul></li>
12508      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12509      * The callback is passed the following parameters:<ul>
12510      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12511      * <li>options {Object} The parameter to the request call.</li>
12512      * </ul></li>
12513      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12514      * for the callback function. Defaults to the browser window.</li>
12515      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12516      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12517      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12518      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12519      * params for the post data. Any params will be appended to the URL.</li>
12520      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12521      * </ul>
12522      * @return {Number} transactionId
12523      */
12524     request : function(o){
12525         if(this.fireEvent("beforerequest", this, o) !== false){
12526             var p = o.params;
12527
12528             if(typeof p == "function"){
12529                 p = p.call(o.scope||window, o);
12530             }
12531             if(typeof p == "object"){
12532                 p = Roo.urlEncode(o.params);
12533             }
12534             if(this.extraParams){
12535                 var extras = Roo.urlEncode(this.extraParams);
12536                 p = p ? (p + '&' + extras) : extras;
12537             }
12538
12539             var url = o.url || this.url;
12540             if(typeof url == 'function'){
12541                 url = url.call(o.scope||window, o);
12542             }
12543
12544             if(o.form){
12545                 var form = Roo.getDom(o.form);
12546                 url = url || form.action;
12547
12548                 var enctype = form.getAttribute("enctype");
12549                 
12550                 if (o.formData) {
12551                     return this.doFormDataUpload(o, url);
12552                 }
12553                 
12554                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12555                     return this.doFormUpload(o, p, url);
12556                 }
12557                 var f = Roo.lib.Ajax.serializeForm(form);
12558                 p = p ? (p + '&' + f) : f;
12559             }
12560             
12561             if (!o.form && o.formData) {
12562                 o.formData = o.formData === true ? new FormData() : o.formData;
12563                 for (var k in o.params) {
12564                     o.formData.append(k,o.params[k]);
12565                 }
12566                     
12567                 return this.doFormDataUpload(o, url);
12568             }
12569             
12570
12571             var hs = o.headers;
12572             if(this.defaultHeaders){
12573                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12574                 if(!o.headers){
12575                     o.headers = hs;
12576                 }
12577             }
12578
12579             var cb = {
12580                 success: this.handleResponse,
12581                 failure: this.handleFailure,
12582                 scope: this,
12583                 argument: {options: o},
12584                 timeout : o.timeout || this.timeout
12585             };
12586
12587             var method = o.method||this.method||(p ? "POST" : "GET");
12588
12589             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12590                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12591             }
12592
12593             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12594                 if(o.autoAbort){
12595                     this.abort();
12596                 }
12597             }else if(this.autoAbort !== false){
12598                 this.abort();
12599             }
12600
12601             if((method == 'GET' && p) || o.xmlData){
12602                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12603                 p = '';
12604             }
12605             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12606             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12607             Roo.lib.Ajax.useDefaultHeader == true;
12608             return this.transId;
12609         }else{
12610             Roo.callback(o.callback, o.scope, [o, null, null]);
12611             return null;
12612         }
12613     },
12614
12615     /**
12616      * Determine whether this object has a request outstanding.
12617      * @param {Number} transactionId (Optional) defaults to the last transaction
12618      * @return {Boolean} True if there is an outstanding request.
12619      */
12620     isLoading : function(transId){
12621         if(transId){
12622             return Roo.lib.Ajax.isCallInProgress(transId);
12623         }else{
12624             return this.transId ? true : false;
12625         }
12626     },
12627
12628     /**
12629      * Aborts any outstanding request.
12630      * @param {Number} transactionId (Optional) defaults to the last transaction
12631      */
12632     abort : function(transId){
12633         if(transId || this.isLoading()){
12634             Roo.lib.Ajax.abort(transId || this.transId);
12635         }
12636     },
12637
12638     // private
12639     handleResponse : function(response){
12640         this.transId = false;
12641         var options = response.argument.options;
12642         response.argument = options ? options.argument : null;
12643         this.fireEvent("requestcomplete", this, response, options);
12644         Roo.callback(options.success, options.scope, [response, options]);
12645         Roo.callback(options.callback, options.scope, [options, true, response]);
12646     },
12647
12648     // private
12649     handleFailure : function(response, e){
12650         this.transId = false;
12651         var options = response.argument.options;
12652         response.argument = options ? options.argument : null;
12653         this.fireEvent("requestexception", this, response, options, e);
12654         Roo.callback(options.failure, options.scope, [response, options]);
12655         Roo.callback(options.callback, options.scope, [options, false, response]);
12656     },
12657
12658     // private
12659     doFormUpload : function(o, ps, url){
12660         var id = Roo.id();
12661         var frame = document.createElement('iframe');
12662         frame.id = id;
12663         frame.name = id;
12664         frame.className = 'x-hidden';
12665         if(Roo.isIE){
12666             frame.src = Roo.SSL_SECURE_URL;
12667         }
12668         document.body.appendChild(frame);
12669
12670         if(Roo.isIE){
12671            document.frames[id].name = id;
12672         }
12673
12674         var form = Roo.getDom(o.form);
12675         form.target = id;
12676         form.method = 'POST';
12677         form.enctype = form.encoding = 'multipart/form-data';
12678         if(url){
12679             form.action = url;
12680         }
12681
12682         var hiddens, hd;
12683         if(ps){ // add dynamic params
12684             hiddens = [];
12685             ps = Roo.urlDecode(ps, false);
12686             for(var k in ps){
12687                 if(ps.hasOwnProperty(k)){
12688                     hd = document.createElement('input');
12689                     hd.type = 'hidden';
12690                     hd.name = k;
12691                     hd.value = ps[k];
12692                     form.appendChild(hd);
12693                     hiddens.push(hd);
12694                 }
12695             }
12696         }
12697
12698         function cb(){
12699             var r = {  // bogus response object
12700                 responseText : '',
12701                 responseXML : null
12702             };
12703
12704             r.argument = o ? o.argument : null;
12705
12706             try { //
12707                 var doc;
12708                 if(Roo.isIE){
12709                     doc = frame.contentWindow.document;
12710                 }else {
12711                     doc = (frame.contentDocument || window.frames[id].document);
12712                 }
12713                 if(doc && doc.body){
12714                     r.responseText = doc.body.innerHTML;
12715                 }
12716                 if(doc && doc.XMLDocument){
12717                     r.responseXML = doc.XMLDocument;
12718                 }else {
12719                     r.responseXML = doc;
12720                 }
12721             }
12722             catch(e) {
12723                 // ignore
12724             }
12725
12726             Roo.EventManager.removeListener(frame, 'load', cb, this);
12727
12728             this.fireEvent("requestcomplete", this, r, o);
12729             Roo.callback(o.success, o.scope, [r, o]);
12730             Roo.callback(o.callback, o.scope, [o, true, r]);
12731
12732             setTimeout(function(){document.body.removeChild(frame);}, 100);
12733         }
12734
12735         Roo.EventManager.on(frame, 'load', cb, this);
12736         form.submit();
12737
12738         if(hiddens){ // remove dynamic params
12739             for(var i = 0, len = hiddens.length; i < len; i++){
12740                 form.removeChild(hiddens[i]);
12741             }
12742         }
12743     },
12744     // this is a 'formdata version???'
12745     
12746     
12747     doFormDataUpload : function(o,  url)
12748     {
12749         var formData;
12750         if (o.form) {
12751             var form =  Roo.getDom(o.form);
12752             form.enctype = form.encoding = 'multipart/form-data';
12753             formData = o.formData === true ? new FormData(form) : o.formData;
12754         } else {
12755             formData = o.formData === true ? new FormData() : o.formData;
12756         }
12757         
12758       
12759         var cb = {
12760             success: this.handleResponse,
12761             failure: this.handleFailure,
12762             scope: this,
12763             argument: {options: o},
12764             timeout : o.timeout || this.timeout
12765         };
12766  
12767         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12768             if(o.autoAbort){
12769                 this.abort();
12770             }
12771         }else if(this.autoAbort !== false){
12772             this.abort();
12773         }
12774
12775         //Roo.lib.Ajax.defaultPostHeader = null;
12776         Roo.lib.Ajax.useDefaultHeader = false;
12777         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12778         Roo.lib.Ajax.useDefaultHeader = true;
12779  
12780          
12781     }
12782     
12783 });
12784 /*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794  
12795 /**
12796  * Global Ajax request class.
12797  * 
12798  * @class Roo.Ajax
12799  * @extends Roo.data.Connection
12800  * @static
12801  * 
12802  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12803  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12804  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12805  * @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)
12806  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12807  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12808  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12809  */
12810 Roo.Ajax = new Roo.data.Connection({
12811     // fix up the docs
12812     /**
12813      * @scope Roo.Ajax
12814      * @type {Boolear} 
12815      */
12816     autoAbort : false,
12817
12818     /**
12819      * Serialize the passed form into a url encoded string
12820      * @scope Roo.Ajax
12821      * @param {String/HTMLElement} form
12822      * @return {String}
12823      */
12824     serializeForm : function(form){
12825         return Roo.lib.Ajax.serializeForm(form);
12826     }
12827 });/*
12828  * Based on:
12829  * Ext JS Library 1.1.1
12830  * Copyright(c) 2006-2007, Ext JS, LLC.
12831  *
12832  * Originally Released Under LGPL - original licence link has changed is not relivant.
12833  *
12834  * Fork - LGPL
12835  * <script type="text/javascript">
12836  */
12837
12838  
12839 /**
12840  * @class Roo.UpdateManager
12841  * @extends Roo.util.Observable
12842  * Provides AJAX-style update for Element object.<br><br>
12843  * Usage:<br>
12844  * <pre><code>
12845  * // Get it from a Roo.Element object
12846  * var el = Roo.get("foo");
12847  * var mgr = el.getUpdateManager();
12848  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12849  * ...
12850  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12851  * <br>
12852  * // or directly (returns the same UpdateManager instance)
12853  * var mgr = new Roo.UpdateManager("myElementId");
12854  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12855  * mgr.on("update", myFcnNeedsToKnow);
12856  * <br>
12857    // short handed call directly from the element object
12858    Roo.get("foo").load({
12859         url: "bar.php",
12860         scripts:true,
12861         params: "for=bar",
12862         text: "Loading Foo..."
12863    });
12864  * </code></pre>
12865  * @constructor
12866  * Create new UpdateManager directly.
12867  * @param {String/HTMLElement/Roo.Element} el The element to update
12868  * @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).
12869  */
12870 Roo.UpdateManager = function(el, forceNew){
12871     el = Roo.get(el);
12872     if(!forceNew && el.updateManager){
12873         return el.updateManager;
12874     }
12875     /**
12876      * The Element object
12877      * @type Roo.Element
12878      */
12879     this.el = el;
12880     /**
12881      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12882      * @type String
12883      */
12884     this.defaultUrl = null;
12885
12886     this.addEvents({
12887         /**
12888          * @event beforeupdate
12889          * Fired before an update is made, return false from your handler and the update is cancelled.
12890          * @param {Roo.Element} el
12891          * @param {String/Object/Function} url
12892          * @param {String/Object} params
12893          */
12894         "beforeupdate": true,
12895         /**
12896          * @event update
12897          * Fired after successful update is made.
12898          * @param {Roo.Element} el
12899          * @param {Object} oResponseObject The response Object
12900          */
12901         "update": true,
12902         /**
12903          * @event failure
12904          * Fired on update failure.
12905          * @param {Roo.Element} el
12906          * @param {Object} oResponseObject The response Object
12907          */
12908         "failure": true
12909     });
12910     var d = Roo.UpdateManager.defaults;
12911     /**
12912      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12913      * @type String
12914      */
12915     this.sslBlankUrl = d.sslBlankUrl;
12916     /**
12917      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12918      * @type Boolean
12919      */
12920     this.disableCaching = d.disableCaching;
12921     /**
12922      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12923      * @type String
12924      */
12925     this.indicatorText = d.indicatorText;
12926     /**
12927      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12928      * @type String
12929      */
12930     this.showLoadIndicator = d.showLoadIndicator;
12931     /**
12932      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12933      * @type Number
12934      */
12935     this.timeout = d.timeout;
12936
12937     /**
12938      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12939      * @type Boolean
12940      */
12941     this.loadScripts = d.loadScripts;
12942
12943     /**
12944      * Transaction object of current executing transaction
12945      */
12946     this.transaction = null;
12947
12948     /**
12949      * @private
12950      */
12951     this.autoRefreshProcId = null;
12952     /**
12953      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12954      * @type Function
12955      */
12956     this.refreshDelegate = this.refresh.createDelegate(this);
12957     /**
12958      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12959      * @type Function
12960      */
12961     this.updateDelegate = this.update.createDelegate(this);
12962     /**
12963      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12964      * @type Function
12965      */
12966     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12967     /**
12968      * @private
12969      */
12970     this.successDelegate = this.processSuccess.createDelegate(this);
12971     /**
12972      * @private
12973      */
12974     this.failureDelegate = this.processFailure.createDelegate(this);
12975
12976     if(!this.renderer){
12977      /**
12978       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12979       */
12980     this.renderer = new Roo.UpdateManager.BasicRenderer();
12981     }
12982     
12983     Roo.UpdateManager.superclass.constructor.call(this);
12984 };
12985
12986 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12987     /**
12988      * Get the Element this UpdateManager is bound to
12989      * @return {Roo.Element} The element
12990      */
12991     getEl : function(){
12992         return this.el;
12993     },
12994     /**
12995      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12996      * @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:
12997 <pre><code>
12998 um.update({<br/>
12999     url: "your-url.php",<br/>
13000     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13001     callback: yourFunction,<br/>
13002     scope: yourObject, //(optional scope)  <br/>
13003     discardUrl: false, <br/>
13004     nocache: false,<br/>
13005     text: "Loading...",<br/>
13006     timeout: 30,<br/>
13007     scripts: false<br/>
13008 });
13009 </code></pre>
13010      * The only required property is url. The optional properties nocache, text and scripts
13011      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13012      * @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}
13013      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13014      * @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.
13015      */
13016     update : function(url, params, callback, discardUrl){
13017         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13018             var method = this.method,
13019                 cfg;
13020             if(typeof url == "object"){ // must be config object
13021                 cfg = url;
13022                 url = cfg.url;
13023                 params = params || cfg.params;
13024                 callback = callback || cfg.callback;
13025                 discardUrl = discardUrl || cfg.discardUrl;
13026                 if(callback && cfg.scope){
13027                     callback = callback.createDelegate(cfg.scope);
13028                 }
13029                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13030                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13031                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13032                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13033                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13034             }
13035             this.showLoading();
13036             if(!discardUrl){
13037                 this.defaultUrl = url;
13038             }
13039             if(typeof url == "function"){
13040                 url = url.call(this);
13041             }
13042
13043             method = method || (params ? "POST" : "GET");
13044             if(method == "GET"){
13045                 url = this.prepareUrl(url);
13046             }
13047
13048             var o = Roo.apply(cfg ||{}, {
13049                 url : url,
13050                 params: params,
13051                 success: this.successDelegate,
13052                 failure: this.failureDelegate,
13053                 callback: undefined,
13054                 timeout: (this.timeout*1000),
13055                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13056             });
13057             Roo.log("updated manager called with timeout of " + o.timeout);
13058             this.transaction = Roo.Ajax.request(o);
13059         }
13060     },
13061
13062     /**
13063      * 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.
13064      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13065      * @param {String/HTMLElement} form The form Id or form element
13066      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13067      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13068      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13069      */
13070     formUpdate : function(form, url, reset, callback){
13071         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13072             if(typeof url == "function"){
13073                 url = url.call(this);
13074             }
13075             form = Roo.getDom(form);
13076             this.transaction = Roo.Ajax.request({
13077                 form: form,
13078                 url:url,
13079                 success: this.successDelegate,
13080                 failure: this.failureDelegate,
13081                 timeout: (this.timeout*1000),
13082                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13083             });
13084             this.showLoading.defer(1, this);
13085         }
13086     },
13087
13088     /**
13089      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13090      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13091      */
13092     refresh : function(callback){
13093         if(this.defaultUrl == null){
13094             return;
13095         }
13096         this.update(this.defaultUrl, null, callback, true);
13097     },
13098
13099     /**
13100      * Set this element to auto refresh.
13101      * @param {Number} interval How often to update (in seconds).
13102      * @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)
13103      * @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}
13104      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13105      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13106      */
13107     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13108         if(refreshNow){
13109             this.update(url || this.defaultUrl, params, callback, true);
13110         }
13111         if(this.autoRefreshProcId){
13112             clearInterval(this.autoRefreshProcId);
13113         }
13114         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13115     },
13116
13117     /**
13118      * Stop auto refresh on this element.
13119      */
13120      stopAutoRefresh : function(){
13121         if(this.autoRefreshProcId){
13122             clearInterval(this.autoRefreshProcId);
13123             delete this.autoRefreshProcId;
13124         }
13125     },
13126
13127     isAutoRefreshing : function(){
13128        return this.autoRefreshProcId ? true : false;
13129     },
13130     /**
13131      * Called to update the element to "Loading" state. Override to perform custom action.
13132      */
13133     showLoading : function(){
13134         if(this.showLoadIndicator){
13135             this.el.update(this.indicatorText);
13136         }
13137     },
13138
13139     /**
13140      * Adds unique parameter to query string if disableCaching = true
13141      * @private
13142      */
13143     prepareUrl : function(url){
13144         if(this.disableCaching){
13145             var append = "_dc=" + (new Date().getTime());
13146             if(url.indexOf("?") !== -1){
13147                 url += "&" + append;
13148             }else{
13149                 url += "?" + append;
13150             }
13151         }
13152         return url;
13153     },
13154
13155     /**
13156      * @private
13157      */
13158     processSuccess : function(response){
13159         this.transaction = null;
13160         if(response.argument.form && response.argument.reset){
13161             try{ // put in try/catch since some older FF releases had problems with this
13162                 response.argument.form.reset();
13163             }catch(e){}
13164         }
13165         if(this.loadScripts){
13166             this.renderer.render(this.el, response, this,
13167                 this.updateComplete.createDelegate(this, [response]));
13168         }else{
13169             this.renderer.render(this.el, response, this);
13170             this.updateComplete(response);
13171         }
13172     },
13173
13174     updateComplete : function(response){
13175         this.fireEvent("update", this.el, response);
13176         if(typeof response.argument.callback == "function"){
13177             response.argument.callback(this.el, true, response);
13178         }
13179     },
13180
13181     /**
13182      * @private
13183      */
13184     processFailure : function(response){
13185         this.transaction = null;
13186         this.fireEvent("failure", this.el, response);
13187         if(typeof response.argument.callback == "function"){
13188             response.argument.callback(this.el, false, response);
13189         }
13190     },
13191
13192     /**
13193      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13194      * @param {Object} renderer The object implementing the render() method
13195      */
13196     setRenderer : function(renderer){
13197         this.renderer = renderer;
13198     },
13199
13200     getRenderer : function(){
13201        return this.renderer;
13202     },
13203
13204     /**
13205      * Set the defaultUrl used for updates
13206      * @param {String/Function} defaultUrl The url or a function to call to get the url
13207      */
13208     setDefaultUrl : function(defaultUrl){
13209         this.defaultUrl = defaultUrl;
13210     },
13211
13212     /**
13213      * Aborts the executing transaction
13214      */
13215     abort : function(){
13216         if(this.transaction){
13217             Roo.Ajax.abort(this.transaction);
13218         }
13219     },
13220
13221     /**
13222      * Returns true if an update is in progress
13223      * @return {Boolean}
13224      */
13225     isUpdating : function(){
13226         if(this.transaction){
13227             return Roo.Ajax.isLoading(this.transaction);
13228         }
13229         return false;
13230     }
13231 });
13232
13233 /**
13234  * @class Roo.UpdateManager.defaults
13235  * @static (not really - but it helps the doc tool)
13236  * The defaults collection enables customizing the default properties of UpdateManager
13237  */
13238    Roo.UpdateManager.defaults = {
13239        /**
13240          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13241          * @type Number
13242          */
13243          timeout : 30,
13244
13245          /**
13246          * True to process scripts by default (Defaults to false).
13247          * @type Boolean
13248          */
13249         loadScripts : false,
13250
13251         /**
13252         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13253         * @type String
13254         */
13255         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13256         /**
13257          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13258          * @type Boolean
13259          */
13260         disableCaching : false,
13261         /**
13262          * Whether to show indicatorText when loading (Defaults to true).
13263          * @type Boolean
13264          */
13265         showLoadIndicator : true,
13266         /**
13267          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13268          * @type String
13269          */
13270         indicatorText : '<div class="loading-indicator">Loading...</div>'
13271    };
13272
13273 /**
13274  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13275  *Usage:
13276  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13277  * @param {String/HTMLElement/Roo.Element} el The element to update
13278  * @param {String} url The url
13279  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13280  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13281  * @static
13282  * @deprecated
13283  * @member Roo.UpdateManager
13284  */
13285 Roo.UpdateManager.updateElement = function(el, url, params, options){
13286     var um = Roo.get(el, true).getUpdateManager();
13287     Roo.apply(um, options);
13288     um.update(url, params, options ? options.callback : null);
13289 };
13290 // alias for backwards compat
13291 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13292 /**
13293  * @class Roo.UpdateManager.BasicRenderer
13294  * Default Content renderer. Updates the elements innerHTML with the responseText.
13295  */
13296 Roo.UpdateManager.BasicRenderer = function(){};
13297
13298 Roo.UpdateManager.BasicRenderer.prototype = {
13299     /**
13300      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13301      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13302      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13303      * @param {Roo.Element} el The element being rendered
13304      * @param {Object} response The YUI Connect response object
13305      * @param {UpdateManager} updateManager The calling update manager
13306      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13307      */
13308      render : function(el, response, updateManager, callback){
13309         el.update(response.responseText, updateManager.loadScripts, callback);
13310     }
13311 };
13312 /*
13313  * Based on:
13314  * Roo JS
13315  * (c)) Alan Knowles
13316  * Licence : LGPL
13317  */
13318
13319
13320 /**
13321  * @class Roo.DomTemplate
13322  * @extends Roo.Template
13323  * An effort at a dom based template engine..
13324  *
13325  * Similar to XTemplate, except it uses dom parsing to create the template..
13326  *
13327  * Supported features:
13328  *
13329  *  Tags:
13330
13331 <pre><code>
13332       {a_variable} - output encoded.
13333       {a_variable.format:("Y-m-d")} - call a method on the variable
13334       {a_variable:raw} - unencoded output
13335       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13336       {a_variable:this.method_on_template(...)} - call a method on the template object.
13337  
13338 </code></pre>
13339  *  The tpl tag:
13340 <pre><code>
13341         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13342         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13343         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13344         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13345   
13346 </code></pre>
13347  *      
13348  */
13349 Roo.DomTemplate = function()
13350 {
13351      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13352      if (this.html) {
13353         this.compile();
13354      }
13355 };
13356
13357
13358 Roo.extend(Roo.DomTemplate, Roo.Template, {
13359     /**
13360      * id counter for sub templates.
13361      */
13362     id : 0,
13363     /**
13364      * flag to indicate if dom parser is inside a pre,
13365      * it will strip whitespace if not.
13366      */
13367     inPre : false,
13368     
13369     /**
13370      * The various sub templates
13371      */
13372     tpls : false,
13373     
13374     
13375     
13376     /**
13377      *
13378      * basic tag replacing syntax
13379      * WORD:WORD()
13380      *
13381      * // you can fake an object call by doing this
13382      *  x.t:(test,tesT) 
13383      * 
13384      */
13385     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13386     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13387     
13388     iterChild : function (node, method) {
13389         
13390         var oldPre = this.inPre;
13391         if (node.tagName == 'PRE') {
13392             this.inPre = true;
13393         }
13394         for( var i = 0; i < node.childNodes.length; i++) {
13395             method.call(this, node.childNodes[i]);
13396         }
13397         this.inPre = oldPre;
13398     },
13399     
13400     
13401     
13402     /**
13403      * compile the template
13404      *
13405      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13406      *
13407      */
13408     compile: function()
13409     {
13410         var s = this.html;
13411         
13412         // covert the html into DOM...
13413         var doc = false;
13414         var div =false;
13415         try {
13416             doc = document.implementation.createHTMLDocument("");
13417             doc.documentElement.innerHTML =   this.html  ;
13418             div = doc.documentElement;
13419         } catch (e) {
13420             // old IE... - nasty -- it causes all sorts of issues.. with
13421             // images getting pulled from server..
13422             div = document.createElement('div');
13423             div.innerHTML = this.html;
13424         }
13425         //doc.documentElement.innerHTML = htmlBody
13426          
13427         
13428         
13429         this.tpls = [];
13430         var _t = this;
13431         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13432         
13433         var tpls = this.tpls;
13434         
13435         // create a top level template from the snippet..
13436         
13437         //Roo.log(div.innerHTML);
13438         
13439         var tpl = {
13440             uid : 'master',
13441             id : this.id++,
13442             attr : false,
13443             value : false,
13444             body : div.innerHTML,
13445             
13446             forCall : false,
13447             execCall : false,
13448             dom : div,
13449             isTop : true
13450             
13451         };
13452         tpls.unshift(tpl);
13453         
13454         
13455         // compile them...
13456         this.tpls = [];
13457         Roo.each(tpls, function(tp){
13458             this.compileTpl(tp);
13459             this.tpls[tp.id] = tp;
13460         }, this);
13461         
13462         this.master = tpls[0];
13463         return this;
13464         
13465         
13466     },
13467     
13468     compileNode : function(node, istop) {
13469         // test for
13470         //Roo.log(node);
13471         
13472         
13473         // skip anything not a tag..
13474         if (node.nodeType != 1) {
13475             if (node.nodeType == 3 && !this.inPre) {
13476                 // reduce white space..
13477                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13478                 
13479             }
13480             return;
13481         }
13482         
13483         var tpl = {
13484             uid : false,
13485             id : false,
13486             attr : false,
13487             value : false,
13488             body : '',
13489             
13490             forCall : false,
13491             execCall : false,
13492             dom : false,
13493             isTop : istop
13494             
13495             
13496         };
13497         
13498         
13499         switch(true) {
13500             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13501             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13502             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13503             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13504             // no default..
13505         }
13506         
13507         
13508         if (!tpl.attr) {
13509             // just itterate children..
13510             this.iterChild(node,this.compileNode);
13511             return;
13512         }
13513         tpl.uid = this.id++;
13514         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13515         node.removeAttribute('roo-'+ tpl.attr);
13516         if (tpl.attr != 'name') {
13517             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13518             node.parentNode.replaceChild(placeholder,  node);
13519         } else {
13520             
13521             var placeholder =  document.createElement('span');
13522             placeholder.className = 'roo-tpl-' + tpl.value;
13523             node.parentNode.replaceChild(placeholder,  node);
13524         }
13525         
13526         // parent now sees '{domtplXXXX}
13527         this.iterChild(node,this.compileNode);
13528         
13529         // we should now have node body...
13530         var div = document.createElement('div');
13531         div.appendChild(node);
13532         tpl.dom = node;
13533         // this has the unfortunate side effect of converting tagged attributes
13534         // eg. href="{...}" into %7C...%7D
13535         // this has been fixed by searching for those combo's although it's a bit hacky..
13536         
13537         
13538         tpl.body = div.innerHTML;
13539         
13540         
13541          
13542         tpl.id = tpl.uid;
13543         switch(tpl.attr) {
13544             case 'for' :
13545                 switch (tpl.value) {
13546                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13547                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13548                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13549                 }
13550                 break;
13551             
13552             case 'exec':
13553                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13554                 break;
13555             
13556             case 'if':     
13557                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13558                 break;
13559             
13560             case 'name':
13561                 tpl.id  = tpl.value; // replace non characters???
13562                 break;
13563             
13564         }
13565         
13566         
13567         this.tpls.push(tpl);
13568         
13569         
13570         
13571     },
13572     
13573     
13574     
13575     
13576     /**
13577      * Compile a segment of the template into a 'sub-template'
13578      *
13579      * 
13580      * 
13581      *
13582      */
13583     compileTpl : function(tpl)
13584     {
13585         var fm = Roo.util.Format;
13586         var useF = this.disableFormats !== true;
13587         
13588         var sep = Roo.isGecko ? "+\n" : ",\n";
13589         
13590         var undef = function(str) {
13591             Roo.debug && Roo.log("Property not found :"  + str);
13592             return '';
13593         };
13594           
13595         //Roo.log(tpl.body);
13596         
13597         
13598         
13599         var fn = function(m, lbrace, name, format, args)
13600         {
13601             //Roo.log("ARGS");
13602             //Roo.log(arguments);
13603             args = args ? args.replace(/\\'/g,"'") : args;
13604             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13605             if (typeof(format) == 'undefined') {
13606                 format =  'htmlEncode'; 
13607             }
13608             if (format == 'raw' ) {
13609                 format = false;
13610             }
13611             
13612             if(name.substr(0, 6) == 'domtpl'){
13613                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13614             }
13615             
13616             // build an array of options to determine if value is undefined..
13617             
13618             // basically get 'xxxx.yyyy' then do
13619             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13620             //    (function () { Roo.log("Property not found"); return ''; })() :
13621             //    ......
13622             
13623             var udef_ar = [];
13624             var lookfor = '';
13625             Roo.each(name.split('.'), function(st) {
13626                 lookfor += (lookfor.length ? '.': '') + st;
13627                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13628             });
13629             
13630             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13631             
13632             
13633             if(format && useF){
13634                 
13635                 args = args ? ',' + args : "";
13636                  
13637                 if(format.substr(0, 5) != "this."){
13638                     format = "fm." + format + '(';
13639                 }else{
13640                     format = 'this.call("'+ format.substr(5) + '", ';
13641                     args = ", values";
13642                 }
13643                 
13644                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13645             }
13646              
13647             if (args && args.length) {
13648                 // called with xxyx.yuu:(test,test)
13649                 // change to ()
13650                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13651             }
13652             // raw.. - :raw modifier..
13653             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13654             
13655         };
13656         var body;
13657         // branched to use + in gecko and [].join() in others
13658         if(Roo.isGecko){
13659             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13660                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13661                     "';};};";
13662         }else{
13663             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13664             body.push(tpl.body.replace(/(\r\n|\n)/g,
13665                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13666             body.push("'].join('');};};");
13667             body = body.join('');
13668         }
13669         
13670         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13671        
13672         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13673         eval(body);
13674         
13675         return this;
13676     },
13677      
13678     /**
13679      * same as applyTemplate, except it's done to one of the subTemplates
13680      * when using named templates, you can do:
13681      *
13682      * var str = pl.applySubTemplate('your-name', values);
13683      *
13684      * 
13685      * @param {Number} id of the template
13686      * @param {Object} values to apply to template
13687      * @param {Object} parent (normaly the instance of this object)
13688      */
13689     applySubTemplate : function(id, values, parent)
13690     {
13691         
13692         
13693         var t = this.tpls[id];
13694         
13695         
13696         try { 
13697             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13698                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13699                 return '';
13700             }
13701         } catch(e) {
13702             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13703             Roo.log(values);
13704           
13705             return '';
13706         }
13707         try { 
13708             
13709             if(t.execCall && t.execCall.call(this, values, parent)){
13710                 return '';
13711             }
13712         } catch(e) {
13713             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13714             Roo.log(values);
13715             return '';
13716         }
13717         
13718         try {
13719             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13720             parent = t.target ? values : parent;
13721             if(t.forCall && vs instanceof Array){
13722                 var buf = [];
13723                 for(var i = 0, len = vs.length; i < len; i++){
13724                     try {
13725                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13726                     } catch (e) {
13727                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13728                         Roo.log(e.body);
13729                         //Roo.log(t.compiled);
13730                         Roo.log(vs[i]);
13731                     }   
13732                 }
13733                 return buf.join('');
13734             }
13735         } catch (e) {
13736             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13737             Roo.log(values);
13738             return '';
13739         }
13740         try {
13741             return t.compiled.call(this, vs, parent);
13742         } catch (e) {
13743             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13744             Roo.log(e.body);
13745             //Roo.log(t.compiled);
13746             Roo.log(values);
13747             return '';
13748         }
13749     },
13750
13751    
13752
13753     applyTemplate : function(values){
13754         return this.master.compiled.call(this, values, {});
13755         //var s = this.subs;
13756     },
13757
13758     apply : function(){
13759         return this.applyTemplate.apply(this, arguments);
13760     }
13761
13762  });
13763
13764 Roo.DomTemplate.from = function(el){
13765     el = Roo.getDom(el);
13766     return new Roo.Domtemplate(el.value || el.innerHTML);
13767 };/*
13768  * Based on:
13769  * Ext JS Library 1.1.1
13770  * Copyright(c) 2006-2007, Ext JS, LLC.
13771  *
13772  * Originally Released Under LGPL - original licence link has changed is not relivant.
13773  *
13774  * Fork - LGPL
13775  * <script type="text/javascript">
13776  */
13777
13778 /**
13779  * @class Roo.util.DelayedTask
13780  * Provides a convenient method of performing setTimeout where a new
13781  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13782  * You can use this class to buffer
13783  * the keypress events for a certain number of milliseconds, and perform only if they stop
13784  * for that amount of time.
13785  * @constructor The parameters to this constructor serve as defaults and are not required.
13786  * @param {Function} fn (optional) The default function to timeout
13787  * @param {Object} scope (optional) The default scope of that timeout
13788  * @param {Array} args (optional) The default Array of arguments
13789  */
13790 Roo.util.DelayedTask = function(fn, scope, args){
13791     var id = null, d, t;
13792
13793     var call = function(){
13794         var now = new Date().getTime();
13795         if(now - t >= d){
13796             clearInterval(id);
13797             id = null;
13798             fn.apply(scope, args || []);
13799         }
13800     };
13801     /**
13802      * Cancels any pending timeout and queues a new one
13803      * @param {Number} delay The milliseconds to delay
13804      * @param {Function} newFn (optional) Overrides function passed to constructor
13805      * @param {Object} newScope (optional) Overrides scope passed to constructor
13806      * @param {Array} newArgs (optional) Overrides args passed to constructor
13807      */
13808     this.delay = function(delay, newFn, newScope, newArgs){
13809         if(id && delay != d){
13810             this.cancel();
13811         }
13812         d = delay;
13813         t = new Date().getTime();
13814         fn = newFn || fn;
13815         scope = newScope || scope;
13816         args = newArgs || args;
13817         if(!id){
13818             id = setInterval(call, d);
13819         }
13820     };
13821
13822     /**
13823      * Cancel the last queued timeout
13824      */
13825     this.cancel = function(){
13826         if(id){
13827             clearInterval(id);
13828             id = null;
13829         }
13830     };
13831 };/*
13832  * Based on:
13833  * Ext JS Library 1.1.1
13834  * Copyright(c) 2006-2007, Ext JS, LLC.
13835  *
13836  * Originally Released Under LGPL - original licence link has changed is not relivant.
13837  *
13838  * Fork - LGPL
13839  * <script type="text/javascript">
13840  */
13841 /**
13842  * @class Roo.util.TaskRunner
13843  * Manage background tasks - not sure why this is better that setInterval?
13844  * @static
13845  *
13846  */
13847  
13848 Roo.util.TaskRunner = function(interval){
13849     interval = interval || 10;
13850     var tasks = [], removeQueue = [];
13851     var id = 0;
13852     var running = false;
13853
13854     var stopThread = function(){
13855         running = false;
13856         clearInterval(id);
13857         id = 0;
13858     };
13859
13860     var startThread = function(){
13861         if(!running){
13862             running = true;
13863             id = setInterval(runTasks, interval);
13864         }
13865     };
13866
13867     var removeTask = function(task){
13868         removeQueue.push(task);
13869         if(task.onStop){
13870             task.onStop();
13871         }
13872     };
13873
13874     var runTasks = function(){
13875         if(removeQueue.length > 0){
13876             for(var i = 0, len = removeQueue.length; i < len; i++){
13877                 tasks.remove(removeQueue[i]);
13878             }
13879             removeQueue = [];
13880             if(tasks.length < 1){
13881                 stopThread();
13882                 return;
13883             }
13884         }
13885         var now = new Date().getTime();
13886         for(var i = 0, len = tasks.length; i < len; ++i){
13887             var t = tasks[i];
13888             var itime = now - t.taskRunTime;
13889             if(t.interval <= itime){
13890                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13891                 t.taskRunTime = now;
13892                 if(rt === false || t.taskRunCount === t.repeat){
13893                     removeTask(t);
13894                     return;
13895                 }
13896             }
13897             if(t.duration && t.duration <= (now - t.taskStartTime)){
13898                 removeTask(t);
13899             }
13900         }
13901     };
13902
13903     /**
13904      * Queues a new task.
13905      * @param {Object} task
13906      *
13907      * Task property : interval = how frequent to run.
13908      * Task object should implement
13909      * function run()
13910      * Task object may implement
13911      * function onStop()
13912      */
13913     this.start = function(task){
13914         tasks.push(task);
13915         task.taskStartTime = new Date().getTime();
13916         task.taskRunTime = 0;
13917         task.taskRunCount = 0;
13918         startThread();
13919         return task;
13920     };
13921     /**
13922      * Stop  new task.
13923      * @param {Object} task
13924      */
13925     this.stop = function(task){
13926         removeTask(task);
13927         return task;
13928     };
13929     /**
13930      * Stop all Tasks
13931      */
13932     this.stopAll = function(){
13933         stopThread();
13934         for(var i = 0, len = tasks.length; i < len; i++){
13935             if(tasks[i].onStop){
13936                 tasks[i].onStop();
13937             }
13938         }
13939         tasks = [];
13940         removeQueue = [];
13941     };
13942 };
13943
13944 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13945  * Based on:
13946  * Ext JS Library 1.1.1
13947  * Copyright(c) 2006-2007, Ext JS, LLC.
13948  *
13949  * Originally Released Under LGPL - original licence link has changed is not relivant.
13950  *
13951  * Fork - LGPL
13952  * <script type="text/javascript">
13953  */
13954
13955  
13956 /**
13957  * @class Roo.util.MixedCollection
13958  * @extends Roo.util.Observable
13959  * A Collection class that maintains both numeric indexes and keys and exposes events.
13960  * @constructor
13961  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13962  * collection (defaults to false)
13963  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13964  * and return the key value for that item.  This is used when available to look up the key on items that
13965  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13966  * equivalent to providing an implementation for the {@link #getKey} method.
13967  */
13968 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13969     this.items = [];
13970     this.map = {};
13971     this.keys = [];
13972     this.length = 0;
13973     this.addEvents({
13974         /**
13975          * @event clear
13976          * Fires when the collection is cleared.
13977          */
13978         "clear" : true,
13979         /**
13980          * @event add
13981          * Fires when an item is added to the collection.
13982          * @param {Number} index The index at which the item was added.
13983          * @param {Object} o The item added.
13984          * @param {String} key The key associated with the added item.
13985          */
13986         "add" : true,
13987         /**
13988          * @event replace
13989          * Fires when an item is replaced in the collection.
13990          * @param {String} key he key associated with the new added.
13991          * @param {Object} old The item being replaced.
13992          * @param {Object} new The new item.
13993          */
13994         "replace" : true,
13995         /**
13996          * @event remove
13997          * Fires when an item is removed from the collection.
13998          * @param {Object} o The item being removed.
13999          * @param {String} key (optional) The key associated with the removed item.
14000          */
14001         "remove" : true,
14002         "sort" : true
14003     });
14004     this.allowFunctions = allowFunctions === true;
14005     if(keyFn){
14006         this.getKey = keyFn;
14007     }
14008     Roo.util.MixedCollection.superclass.constructor.call(this);
14009 };
14010
14011 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14012     allowFunctions : false,
14013     
14014 /**
14015  * Adds an item to the collection.
14016  * @param {String} key The key to associate with the item
14017  * @param {Object} o The item to add.
14018  * @return {Object} The item added.
14019  */
14020     add : function(key, o){
14021         if(arguments.length == 1){
14022             o = arguments[0];
14023             key = this.getKey(o);
14024         }
14025         if(typeof key == "undefined" || key === null){
14026             this.length++;
14027             this.items.push(o);
14028             this.keys.push(null);
14029         }else{
14030             var old = this.map[key];
14031             if(old){
14032                 return this.replace(key, o);
14033             }
14034             this.length++;
14035             this.items.push(o);
14036             this.map[key] = o;
14037             this.keys.push(key);
14038         }
14039         this.fireEvent("add", this.length-1, o, key);
14040         return o;
14041     },
14042        
14043 /**
14044   * MixedCollection has a generic way to fetch keys if you implement getKey.
14045 <pre><code>
14046 // normal way
14047 var mc = new Roo.util.MixedCollection();
14048 mc.add(someEl.dom.id, someEl);
14049 mc.add(otherEl.dom.id, otherEl);
14050 //and so on
14051
14052 // using getKey
14053 var mc = new Roo.util.MixedCollection();
14054 mc.getKey = function(el){
14055    return el.dom.id;
14056 };
14057 mc.add(someEl);
14058 mc.add(otherEl);
14059
14060 // or via the constructor
14061 var mc = new Roo.util.MixedCollection(false, function(el){
14062    return el.dom.id;
14063 });
14064 mc.add(someEl);
14065 mc.add(otherEl);
14066 </code></pre>
14067  * @param o {Object} The item for which to find the key.
14068  * @return {Object} The key for the passed item.
14069  */
14070     getKey : function(o){
14071          return o.id; 
14072     },
14073    
14074 /**
14075  * Replaces an item in the collection.
14076  * @param {String} key The key associated with the item to replace, or the item to replace.
14077  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14078  * @return {Object}  The new item.
14079  */
14080     replace : function(key, o){
14081         if(arguments.length == 1){
14082             o = arguments[0];
14083             key = this.getKey(o);
14084         }
14085         var old = this.item(key);
14086         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14087              return this.add(key, o);
14088         }
14089         var index = this.indexOfKey(key);
14090         this.items[index] = o;
14091         this.map[key] = o;
14092         this.fireEvent("replace", key, old, o);
14093         return o;
14094     },
14095    
14096 /**
14097  * Adds all elements of an Array or an Object to the collection.
14098  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14099  * an Array of values, each of which are added to the collection.
14100  */
14101     addAll : function(objs){
14102         if(arguments.length > 1 || objs instanceof Array){
14103             var args = arguments.length > 1 ? arguments : objs;
14104             for(var i = 0, len = args.length; i < len; i++){
14105                 this.add(args[i]);
14106             }
14107         }else{
14108             for(var key in objs){
14109                 if(this.allowFunctions || typeof objs[key] != "function"){
14110                     this.add(key, objs[key]);
14111                 }
14112             }
14113         }
14114     },
14115    
14116 /**
14117  * Executes the specified function once for every item in the collection, passing each
14118  * item as the first and only parameter. returning false from the function will stop the iteration.
14119  * @param {Function} fn The function to execute for each item.
14120  * @param {Object} scope (optional) The scope in which to execute the function.
14121  */
14122     each : function(fn, scope){
14123         var items = [].concat(this.items); // each safe for removal
14124         for(var i = 0, len = items.length; i < len; i++){
14125             if(fn.call(scope || items[i], items[i], i, len) === false){
14126                 break;
14127             }
14128         }
14129     },
14130    
14131 /**
14132  * Executes the specified function once for every key in the collection, passing each
14133  * key, and its associated item as the first two parameters.
14134  * @param {Function} fn The function to execute for each item.
14135  * @param {Object} scope (optional) The scope in which to execute the function.
14136  */
14137     eachKey : function(fn, scope){
14138         for(var i = 0, len = this.keys.length; i < len; i++){
14139             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14140         }
14141     },
14142    
14143 /**
14144  * Returns the first item in the collection which elicits a true return value from the
14145  * passed selection function.
14146  * @param {Function} fn The selection function to execute for each item.
14147  * @param {Object} scope (optional) The scope in which to execute the function.
14148  * @return {Object} The first item in the collection which returned true from the selection function.
14149  */
14150     find : function(fn, scope){
14151         for(var i = 0, len = this.items.length; i < len; i++){
14152             if(fn.call(scope || window, this.items[i], this.keys[i])){
14153                 return this.items[i];
14154             }
14155         }
14156         return null;
14157     },
14158    
14159 /**
14160  * Inserts an item at the specified index in the collection.
14161  * @param {Number} index The index to insert the item at.
14162  * @param {String} key The key to associate with the new item, or the item itself.
14163  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14164  * @return {Object} The item inserted.
14165  */
14166     insert : function(index, key, o){
14167         if(arguments.length == 2){
14168             o = arguments[1];
14169             key = this.getKey(o);
14170         }
14171         if(index >= this.length){
14172             return this.add(key, o);
14173         }
14174         this.length++;
14175         this.items.splice(index, 0, o);
14176         if(typeof key != "undefined" && key != null){
14177             this.map[key] = o;
14178         }
14179         this.keys.splice(index, 0, key);
14180         this.fireEvent("add", index, o, key);
14181         return o;
14182     },
14183    
14184 /**
14185  * Removed an item from the collection.
14186  * @param {Object} o The item to remove.
14187  * @return {Object} The item removed.
14188  */
14189     remove : function(o){
14190         return this.removeAt(this.indexOf(o));
14191     },
14192    
14193 /**
14194  * Remove an item from a specified index in the collection.
14195  * @param {Number} index The index within the collection of the item to remove.
14196  */
14197     removeAt : function(index){
14198         if(index < this.length && index >= 0){
14199             this.length--;
14200             var o = this.items[index];
14201             this.items.splice(index, 1);
14202             var key = this.keys[index];
14203             if(typeof key != "undefined"){
14204                 delete this.map[key];
14205             }
14206             this.keys.splice(index, 1);
14207             this.fireEvent("remove", o, key);
14208         }
14209     },
14210    
14211 /**
14212  * Removed an item associated with the passed key fom the collection.
14213  * @param {String} key The key of the item to remove.
14214  */
14215     removeKey : function(key){
14216         return this.removeAt(this.indexOfKey(key));
14217     },
14218    
14219 /**
14220  * Returns the number of items in the collection.
14221  * @return {Number} the number of items in the collection.
14222  */
14223     getCount : function(){
14224         return this.length; 
14225     },
14226    
14227 /**
14228  * Returns index within the collection of the passed Object.
14229  * @param {Object} o The item to find the index of.
14230  * @return {Number} index of the item.
14231  */
14232     indexOf : function(o){
14233         if(!this.items.indexOf){
14234             for(var i = 0, len = this.items.length; i < len; i++){
14235                 if(this.items[i] == o) {
14236                     return i;
14237                 }
14238             }
14239             return -1;
14240         }else{
14241             return this.items.indexOf(o);
14242         }
14243     },
14244    
14245 /**
14246  * Returns index within the collection of the passed key.
14247  * @param {String} key The key to find the index of.
14248  * @return {Number} index of the key.
14249  */
14250     indexOfKey : function(key){
14251         if(!this.keys.indexOf){
14252             for(var i = 0, len = this.keys.length; i < len; i++){
14253                 if(this.keys[i] == key) {
14254                     return i;
14255                 }
14256             }
14257             return -1;
14258         }else{
14259             return this.keys.indexOf(key);
14260         }
14261     },
14262    
14263 /**
14264  * Returns the item associated with the passed key OR index. Key has priority over index.
14265  * @param {String/Number} key The key or index of the item.
14266  * @return {Object} The item associated with the passed key.
14267  */
14268     item : function(key){
14269         if (key === 'length') {
14270             return null;
14271         }
14272         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14273         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14274     },
14275     
14276 /**
14277  * Returns the item at the specified index.
14278  * @param {Number} index The index of the item.
14279  * @return {Object}
14280  */
14281     itemAt : function(index){
14282         return this.items[index];
14283     },
14284     
14285 /**
14286  * Returns the item associated with the passed key.
14287  * @param {String/Number} key The key of the item.
14288  * @return {Object} The item associated with the passed key.
14289  */
14290     key : function(key){
14291         return this.map[key];
14292     },
14293    
14294 /**
14295  * Returns true if the collection contains the passed Object as an item.
14296  * @param {Object} o  The Object to look for in the collection.
14297  * @return {Boolean} True if the collection contains the Object as an item.
14298  */
14299     contains : function(o){
14300         return this.indexOf(o) != -1;
14301     },
14302    
14303 /**
14304  * Returns true if the collection contains the passed Object as a key.
14305  * @param {String} key The key to look for in the collection.
14306  * @return {Boolean} True if the collection contains the Object as a key.
14307  */
14308     containsKey : function(key){
14309         return typeof this.map[key] != "undefined";
14310     },
14311    
14312 /**
14313  * Removes all items from the collection.
14314  */
14315     clear : function(){
14316         this.length = 0;
14317         this.items = [];
14318         this.keys = [];
14319         this.map = {};
14320         this.fireEvent("clear");
14321     },
14322    
14323 /**
14324  * Returns the first item in the collection.
14325  * @return {Object} the first item in the collection..
14326  */
14327     first : function(){
14328         return this.items[0]; 
14329     },
14330    
14331 /**
14332  * Returns the last item in the collection.
14333  * @return {Object} the last item in the collection..
14334  */
14335     last : function(){
14336         return this.items[this.length-1];   
14337     },
14338     
14339     _sort : function(property, dir, fn){
14340         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14341         fn = fn || function(a, b){
14342             return a-b;
14343         };
14344         var c = [], k = this.keys, items = this.items;
14345         for(var i = 0, len = items.length; i < len; i++){
14346             c[c.length] = {key: k[i], value: items[i], index: i};
14347         }
14348         c.sort(function(a, b){
14349             var v = fn(a[property], b[property]) * dsc;
14350             if(v == 0){
14351                 v = (a.index < b.index ? -1 : 1);
14352             }
14353             return v;
14354         });
14355         for(var i = 0, len = c.length; i < len; i++){
14356             items[i] = c[i].value;
14357             k[i] = c[i].key;
14358         }
14359         this.fireEvent("sort", this);
14360     },
14361     
14362     /**
14363      * Sorts this collection with the passed comparison function
14364      * @param {String} direction (optional) "ASC" or "DESC"
14365      * @param {Function} fn (optional) comparison function
14366      */
14367     sort : function(dir, fn){
14368         this._sort("value", dir, fn);
14369     },
14370     
14371     /**
14372      * Sorts this collection by keys
14373      * @param {String} direction (optional) "ASC" or "DESC"
14374      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14375      */
14376     keySort : function(dir, fn){
14377         this._sort("key", dir, fn || function(a, b){
14378             return String(a).toUpperCase()-String(b).toUpperCase();
14379         });
14380     },
14381     
14382     /**
14383      * Returns a range of items in this collection
14384      * @param {Number} startIndex (optional) defaults to 0
14385      * @param {Number} endIndex (optional) default to the last item
14386      * @return {Array} An array of items
14387      */
14388     getRange : function(start, end){
14389         var items = this.items;
14390         if(items.length < 1){
14391             return [];
14392         }
14393         start = start || 0;
14394         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14395         var r = [];
14396         if(start <= end){
14397             for(var i = start; i <= end; i++) {
14398                     r[r.length] = items[i];
14399             }
14400         }else{
14401             for(var i = start; i >= end; i--) {
14402                     r[r.length] = items[i];
14403             }
14404         }
14405         return r;
14406     },
14407         
14408     /**
14409      * Filter the <i>objects</i> in this collection by a specific property. 
14410      * Returns a new collection that has been filtered.
14411      * @param {String} property A property on your objects
14412      * @param {String/RegExp} value Either string that the property values 
14413      * should start with or a RegExp to test against the property
14414      * @return {MixedCollection} The new filtered collection
14415      */
14416     filter : function(property, value){
14417         if(!value.exec){ // not a regex
14418             value = String(value);
14419             if(value.length == 0){
14420                 return this.clone();
14421             }
14422             value = new RegExp("^" + Roo.escapeRe(value), "i");
14423         }
14424         return this.filterBy(function(o){
14425             return o && value.test(o[property]);
14426         });
14427         },
14428     
14429     /**
14430      * Filter by a function. * Returns a new collection that has been filtered.
14431      * The passed function will be called with each 
14432      * object in the collection. If the function returns true, the value is included 
14433      * otherwise it is filtered.
14434      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14435      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14436      * @return {MixedCollection} The new filtered collection
14437      */
14438     filterBy : function(fn, scope){
14439         var r = new Roo.util.MixedCollection();
14440         r.getKey = this.getKey;
14441         var k = this.keys, it = this.items;
14442         for(var i = 0, len = it.length; i < len; i++){
14443             if(fn.call(scope||this, it[i], k[i])){
14444                                 r.add(k[i], it[i]);
14445                         }
14446         }
14447         return r;
14448     },
14449     
14450     /**
14451      * Creates a duplicate of this collection
14452      * @return {MixedCollection}
14453      */
14454     clone : function(){
14455         var r = new Roo.util.MixedCollection();
14456         var k = this.keys, it = this.items;
14457         for(var i = 0, len = it.length; i < len; i++){
14458             r.add(k[i], it[i]);
14459         }
14460         r.getKey = this.getKey;
14461         return r;
14462     }
14463 });
14464 /**
14465  * Returns the item associated with the passed key or index.
14466  * @method
14467  * @param {String/Number} key The key or index of the item.
14468  * @return {Object} The item associated with the passed key.
14469  */
14470 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14471  * Based on:
14472  * Ext JS Library 1.1.1
14473  * Copyright(c) 2006-2007, Ext JS, LLC.
14474  *
14475  * Originally Released Under LGPL - original licence link has changed is not relivant.
14476  *
14477  * Fork - LGPL
14478  * <script type="text/javascript">
14479  */
14480 /**
14481  * @class Roo.util.JSON
14482  * Modified version of Douglas Crockford"s json.js that doesn"t
14483  * mess with the Object prototype 
14484  * http://www.json.org/js.html
14485  * @static
14486  */
14487 Roo.util.JSON = new (function(){
14488     var useHasOwn = {}.hasOwnProperty ? true : false;
14489     
14490     // crashes Safari in some instances
14491     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14492     
14493     var pad = function(n) {
14494         return n < 10 ? "0" + n : n;
14495     };
14496     
14497     var m = {
14498         "\b": '\\b',
14499         "\t": '\\t',
14500         "\n": '\\n',
14501         "\f": '\\f',
14502         "\r": '\\r',
14503         '"' : '\\"',
14504         "\\": '\\\\'
14505     };
14506
14507     var encodeString = function(s){
14508         if (/["\\\x00-\x1f]/.test(s)) {
14509             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14510                 var c = m[b];
14511                 if(c){
14512                     return c;
14513                 }
14514                 c = b.charCodeAt();
14515                 return "\\u00" +
14516                     Math.floor(c / 16).toString(16) +
14517                     (c % 16).toString(16);
14518             }) + '"';
14519         }
14520         return '"' + s + '"';
14521     };
14522     
14523     var encodeArray = function(o){
14524         var a = ["["], b, i, l = o.length, v;
14525             for (i = 0; i < l; i += 1) {
14526                 v = o[i];
14527                 switch (typeof v) {
14528                     case "undefined":
14529                     case "function":
14530                     case "unknown":
14531                         break;
14532                     default:
14533                         if (b) {
14534                             a.push(',');
14535                         }
14536                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14537                         b = true;
14538                 }
14539             }
14540             a.push("]");
14541             return a.join("");
14542     };
14543     
14544     var encodeDate = function(o){
14545         return '"' + o.getFullYear() + "-" +
14546                 pad(o.getMonth() + 1) + "-" +
14547                 pad(o.getDate()) + "T" +
14548                 pad(o.getHours()) + ":" +
14549                 pad(o.getMinutes()) + ":" +
14550                 pad(o.getSeconds()) + '"';
14551     };
14552     
14553     /**
14554      * Encodes an Object, Array or other value
14555      * @param {Mixed} o The variable to encode
14556      * @return {String} The JSON string
14557      */
14558     this.encode = function(o)
14559     {
14560         // should this be extended to fully wrap stringify..
14561         
14562         if(typeof o == "undefined" || o === null){
14563             return "null";
14564         }else if(o instanceof Array){
14565             return encodeArray(o);
14566         }else if(o instanceof Date){
14567             return encodeDate(o);
14568         }else if(typeof o == "string"){
14569             return encodeString(o);
14570         }else if(typeof o == "number"){
14571             return isFinite(o) ? String(o) : "null";
14572         }else if(typeof o == "boolean"){
14573             return String(o);
14574         }else {
14575             var a = ["{"], b, i, v;
14576             for (i in o) {
14577                 if(!useHasOwn || o.hasOwnProperty(i)) {
14578                     v = o[i];
14579                     switch (typeof v) {
14580                     case "undefined":
14581                     case "function":
14582                     case "unknown":
14583                         break;
14584                     default:
14585                         if(b){
14586                             a.push(',');
14587                         }
14588                         a.push(this.encode(i), ":",
14589                                 v === null ? "null" : this.encode(v));
14590                         b = true;
14591                     }
14592                 }
14593             }
14594             a.push("}");
14595             return a.join("");
14596         }
14597     };
14598     
14599     /**
14600      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14601      * @param {String} json The JSON string
14602      * @return {Object} The resulting object
14603      */
14604     this.decode = function(json){
14605         
14606         return  /** eval:var:json */ eval("(" + json + ')');
14607     };
14608 })();
14609 /** 
14610  * Shorthand for {@link Roo.util.JSON#encode}
14611  * @member Roo encode 
14612  * @method */
14613 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14614 /** 
14615  * Shorthand for {@link Roo.util.JSON#decode}
14616  * @member Roo decode 
14617  * @method */
14618 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14619 /*
14620  * Based on:
14621  * Ext JS Library 1.1.1
14622  * Copyright(c) 2006-2007, Ext JS, LLC.
14623  *
14624  * Originally Released Under LGPL - original licence link has changed is not relivant.
14625  *
14626  * Fork - LGPL
14627  * <script type="text/javascript">
14628  */
14629  
14630 /**
14631  * @class Roo.util.Format
14632  * Reusable data formatting functions
14633  * @static
14634  */
14635 Roo.util.Format = function(){
14636     var trimRe = /^\s+|\s+$/g;
14637     return {
14638         /**
14639          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14640          * @param {String} value The string to truncate
14641          * @param {Number} length The maximum length to allow before truncating
14642          * @return {String} The converted text
14643          */
14644         ellipsis : function(value, len){
14645             if(value && value.length > len){
14646                 return value.substr(0, len-3)+"...";
14647             }
14648             return value;
14649         },
14650
14651         /**
14652          * Checks a reference and converts it to empty string if it is undefined
14653          * @param {Mixed} value Reference to check
14654          * @return {Mixed} Empty string if converted, otherwise the original value
14655          */
14656         undef : function(value){
14657             return typeof value != "undefined" ? value : "";
14658         },
14659
14660         /**
14661          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14662          * @param {String} value The string to encode
14663          * @return {String} The encoded text
14664          */
14665         htmlEncode : function(value){
14666             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14667         },
14668
14669         /**
14670          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14671          * @param {String} value The string to decode
14672          * @return {String} The decoded text
14673          */
14674         htmlDecode : function(value){
14675             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14676         },
14677
14678         /**
14679          * Trims any whitespace from either side of a string
14680          * @param {String} value The text to trim
14681          * @return {String} The trimmed text
14682          */
14683         trim : function(value){
14684             return String(value).replace(trimRe, "");
14685         },
14686
14687         /**
14688          * Returns a substring from within an original string
14689          * @param {String} value The original text
14690          * @param {Number} start The start index of the substring
14691          * @param {Number} length The length of the substring
14692          * @return {String} The substring
14693          */
14694         substr : function(value, start, length){
14695             return String(value).substr(start, length);
14696         },
14697
14698         /**
14699          * Converts a string to all lower case letters
14700          * @param {String} value The text to convert
14701          * @return {String} The converted text
14702          */
14703         lowercase : function(value){
14704             return String(value).toLowerCase();
14705         },
14706
14707         /**
14708          * Converts a string to all upper case letters
14709          * @param {String} value The text to convert
14710          * @return {String} The converted text
14711          */
14712         uppercase : function(value){
14713             return String(value).toUpperCase();
14714         },
14715
14716         /**
14717          * Converts the first character only of a string to upper case
14718          * @param {String} value The text to convert
14719          * @return {String} The converted text
14720          */
14721         capitalize : function(value){
14722             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14723         },
14724
14725         // private
14726         call : function(value, fn){
14727             if(arguments.length > 2){
14728                 var args = Array.prototype.slice.call(arguments, 2);
14729                 args.unshift(value);
14730                  
14731                 return /** eval:var:value */  eval(fn).apply(window, args);
14732             }else{
14733                 /** eval:var:value */
14734                 return /** eval:var:value */ eval(fn).call(window, value);
14735             }
14736         },
14737
14738        
14739         /**
14740          * safer version of Math.toFixed..??/
14741          * @param {Number/String} value The numeric value to format
14742          * @param {Number/String} value Decimal places 
14743          * @return {String} The formatted currency string
14744          */
14745         toFixed : function(v, n)
14746         {
14747             // why not use to fixed - precision is buggered???
14748             if (!n) {
14749                 return Math.round(v-0);
14750             }
14751             var fact = Math.pow(10,n+1);
14752             v = (Math.round((v-0)*fact))/fact;
14753             var z = (''+fact).substring(2);
14754             if (v == Math.floor(v)) {
14755                 return Math.floor(v) + '.' + z;
14756             }
14757             
14758             // now just padd decimals..
14759             var ps = String(v).split('.');
14760             var fd = (ps[1] + z);
14761             var r = fd.substring(0,n); 
14762             var rm = fd.substring(n); 
14763             if (rm < 5) {
14764                 return ps[0] + '.' + r;
14765             }
14766             r*=1; // turn it into a number;
14767             r++;
14768             if (String(r).length != n) {
14769                 ps[0]*=1;
14770                 ps[0]++;
14771                 r = String(r).substring(1); // chop the end off.
14772             }
14773             
14774             return ps[0] + '.' + r;
14775              
14776         },
14777         
14778         /**
14779          * Format a number as US currency
14780          * @param {Number/String} value The numeric value to format
14781          * @return {String} The formatted currency string
14782          */
14783         usMoney : function(v){
14784             return '$' + Roo.util.Format.number(v);
14785         },
14786         
14787         /**
14788          * Format a number
14789          * eventually this should probably emulate php's number_format
14790          * @param {Number/String} value The numeric value to format
14791          * @param {Number} decimals number of decimal places
14792          * @param {String} delimiter for thousands (default comma)
14793          * @return {String} The formatted currency string
14794          */
14795         number : function(v, decimals, thousandsDelimiter)
14796         {
14797             // multiply and round.
14798             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14799             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14800             
14801             var mul = Math.pow(10, decimals);
14802             var zero = String(mul).substring(1);
14803             v = (Math.round((v-0)*mul))/mul;
14804             
14805             // if it's '0' number.. then
14806             
14807             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14808             v = String(v);
14809             var ps = v.split('.');
14810             var whole = ps[0];
14811             
14812             var r = /(\d+)(\d{3})/;
14813             // add comma's
14814             
14815             if(thousandsDelimiter.length != 0) {
14816                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14817             } 
14818             
14819             var sub = ps[1] ?
14820                     // has decimals..
14821                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14822                     // does not have decimals
14823                     (decimals ? ('.' + zero) : '');
14824             
14825             
14826             return whole + sub ;
14827         },
14828         
14829         /**
14830          * Parse a value into a formatted date using the specified format pattern.
14831          * @param {Mixed} value The value to format
14832          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14833          * @return {String} The formatted date string
14834          */
14835         date : function(v, format){
14836             if(!v){
14837                 return "";
14838             }
14839             if(!(v instanceof Date)){
14840                 v = new Date(Date.parse(v));
14841             }
14842             return v.dateFormat(format || Roo.util.Format.defaults.date);
14843         },
14844
14845         /**
14846          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14847          * @param {String} format Any valid date format string
14848          * @return {Function} The date formatting function
14849          */
14850         dateRenderer : function(format){
14851             return function(v){
14852                 return Roo.util.Format.date(v, format);  
14853             };
14854         },
14855
14856         // private
14857         stripTagsRE : /<\/?[^>]+>/gi,
14858         
14859         /**
14860          * Strips all HTML tags
14861          * @param {Mixed} value The text from which to strip tags
14862          * @return {String} The stripped text
14863          */
14864         stripTags : function(v){
14865             return !v ? v : String(v).replace(this.stripTagsRE, "");
14866         },
14867         
14868         /**
14869          * Size in Mb,Gb etc.
14870          * @param {Number} value The number to be formated
14871          * @param {number} decimals how many decimal places
14872          * @return {String} the formated string
14873          */
14874         size : function(value, decimals)
14875         {
14876             var sizes = ['b', 'k', 'M', 'G', 'T'];
14877             if (value == 0) {
14878                 return 0;
14879             }
14880             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14881             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14882         }
14883         
14884         
14885         
14886     };
14887 }();
14888 Roo.util.Format.defaults = {
14889     date : 'd/M/Y'
14890 };/*
14891  * Based on:
14892  * Ext JS Library 1.1.1
14893  * Copyright(c) 2006-2007, Ext JS, LLC.
14894  *
14895  * Originally Released Under LGPL - original licence link has changed is not relivant.
14896  *
14897  * Fork - LGPL
14898  * <script type="text/javascript">
14899  */
14900
14901
14902  
14903
14904 /**
14905  * @class Roo.MasterTemplate
14906  * @extends Roo.Template
14907  * Provides a template that can have child templates. The syntax is:
14908 <pre><code>
14909 var t = new Roo.MasterTemplate(
14910         '&lt;select name="{name}"&gt;',
14911                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14912         '&lt;/select&gt;'
14913 );
14914 t.add('options', {value: 'foo', text: 'bar'});
14915 // or you can add multiple child elements in one shot
14916 t.addAll('options', [
14917     {value: 'foo', text: 'bar'},
14918     {value: 'foo2', text: 'bar2'},
14919     {value: 'foo3', text: 'bar3'}
14920 ]);
14921 // then append, applying the master template values
14922 t.append('my-form', {name: 'my-select'});
14923 </code></pre>
14924 * A name attribute for the child template is not required if you have only one child
14925 * template or you want to refer to them by index.
14926  */
14927 Roo.MasterTemplate = function(){
14928     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14929     this.originalHtml = this.html;
14930     var st = {};
14931     var m, re = this.subTemplateRe;
14932     re.lastIndex = 0;
14933     var subIndex = 0;
14934     while(m = re.exec(this.html)){
14935         var name = m[1], content = m[2];
14936         st[subIndex] = {
14937             name: name,
14938             index: subIndex,
14939             buffer: [],
14940             tpl : new Roo.Template(content)
14941         };
14942         if(name){
14943             st[name] = st[subIndex];
14944         }
14945         st[subIndex].tpl.compile();
14946         st[subIndex].tpl.call = this.call.createDelegate(this);
14947         subIndex++;
14948     }
14949     this.subCount = subIndex;
14950     this.subs = st;
14951 };
14952 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14953     /**
14954     * The regular expression used to match sub templates
14955     * @type RegExp
14956     * @property
14957     */
14958     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14959
14960     /**
14961      * Applies the passed values to a child template.
14962      * @param {String/Number} name (optional) The name or index of the child template
14963      * @param {Array/Object} values The values to be applied to the template
14964      * @return {MasterTemplate} this
14965      */
14966      add : function(name, values){
14967         if(arguments.length == 1){
14968             values = arguments[0];
14969             name = 0;
14970         }
14971         var s = this.subs[name];
14972         s.buffer[s.buffer.length] = s.tpl.apply(values);
14973         return this;
14974     },
14975
14976     /**
14977      * Applies all the passed values to a child template.
14978      * @param {String/Number} name (optional) The name or index of the child template
14979      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14980      * @param {Boolean} reset (optional) True to reset the template first
14981      * @return {MasterTemplate} this
14982      */
14983     fill : function(name, values, reset){
14984         var a = arguments;
14985         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14986             values = a[0];
14987             name = 0;
14988             reset = a[1];
14989         }
14990         if(reset){
14991             this.reset();
14992         }
14993         for(var i = 0, len = values.length; i < len; i++){
14994             this.add(name, values[i]);
14995         }
14996         return this;
14997     },
14998
14999     /**
15000      * Resets the template for reuse
15001      * @return {MasterTemplate} this
15002      */
15003      reset : function(){
15004         var s = this.subs;
15005         for(var i = 0; i < this.subCount; i++){
15006             s[i].buffer = [];
15007         }
15008         return this;
15009     },
15010
15011     applyTemplate : function(values){
15012         var s = this.subs;
15013         var replaceIndex = -1;
15014         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15015             return s[++replaceIndex].buffer.join("");
15016         });
15017         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15018     },
15019
15020     apply : function(){
15021         return this.applyTemplate.apply(this, arguments);
15022     },
15023
15024     compile : function(){return this;}
15025 });
15026
15027 /**
15028  * Alias for fill().
15029  * @method
15030  */
15031 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15032  /**
15033  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15034  * var tpl = Roo.MasterTemplate.from('element-id');
15035  * @param {String/HTMLElement} el
15036  * @param {Object} config
15037  * @static
15038  */
15039 Roo.MasterTemplate.from = function(el, config){
15040     el = Roo.getDom(el);
15041     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15042 };/*
15043  * Based on:
15044  * Ext JS Library 1.1.1
15045  * Copyright(c) 2006-2007, Ext JS, LLC.
15046  *
15047  * Originally Released Under LGPL - original licence link has changed is not relivant.
15048  *
15049  * Fork - LGPL
15050  * <script type="text/javascript">
15051  */
15052
15053  
15054 /**
15055  * @class Roo.util.CSS
15056  * Utility class for manipulating CSS rules
15057  * @static
15058
15059  */
15060 Roo.util.CSS = function(){
15061         var rules = null;
15062         var doc = document;
15063
15064     var camelRe = /(-[a-z])/gi;
15065     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15066
15067    return {
15068    /**
15069     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15070     * tag and appended to the HEAD of the document.
15071     * @param {String|Object} cssText The text containing the css rules
15072     * @param {String} id An id to add to the stylesheet for later removal
15073     * @return {StyleSheet}
15074     */
15075     createStyleSheet : function(cssText, id){
15076         var ss;
15077         var head = doc.getElementsByTagName("head")[0];
15078         var nrules = doc.createElement("style");
15079         nrules.setAttribute("type", "text/css");
15080         if(id){
15081             nrules.setAttribute("id", id);
15082         }
15083         if (typeof(cssText) != 'string') {
15084             // support object maps..
15085             // not sure if this a good idea.. 
15086             // perhaps it should be merged with the general css handling
15087             // and handle js style props.
15088             var cssTextNew = [];
15089             for(var n in cssText) {
15090                 var citems = [];
15091                 for(var k in cssText[n]) {
15092                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15093                 }
15094                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15095                 
15096             }
15097             cssText = cssTextNew.join("\n");
15098             
15099         }
15100        
15101        
15102        if(Roo.isIE){
15103            head.appendChild(nrules);
15104            ss = nrules.styleSheet;
15105            ss.cssText = cssText;
15106        }else{
15107            try{
15108                 nrules.appendChild(doc.createTextNode(cssText));
15109            }catch(e){
15110                nrules.cssText = cssText; 
15111            }
15112            head.appendChild(nrules);
15113            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15114        }
15115        this.cacheStyleSheet(ss);
15116        return ss;
15117    },
15118
15119    /**
15120     * Removes a style or link tag by id
15121     * @param {String} id The id of the tag
15122     */
15123    removeStyleSheet : function(id){
15124        var existing = doc.getElementById(id);
15125        if(existing){
15126            existing.parentNode.removeChild(existing);
15127        }
15128    },
15129
15130    /**
15131     * Dynamically swaps an existing stylesheet reference for a new one
15132     * @param {String} id The id of an existing link tag to remove
15133     * @param {String} url The href of the new stylesheet to include
15134     */
15135    swapStyleSheet : function(id, url){
15136        this.removeStyleSheet(id);
15137        var ss = doc.createElement("link");
15138        ss.setAttribute("rel", "stylesheet");
15139        ss.setAttribute("type", "text/css");
15140        ss.setAttribute("id", id);
15141        ss.setAttribute("href", url);
15142        doc.getElementsByTagName("head")[0].appendChild(ss);
15143    },
15144    
15145    /**
15146     * Refresh the rule cache if you have dynamically added stylesheets
15147     * @return {Object} An object (hash) of rules indexed by selector
15148     */
15149    refreshCache : function(){
15150        return this.getRules(true);
15151    },
15152
15153    // private
15154    cacheStyleSheet : function(stylesheet){
15155        if(!rules){
15156            rules = {};
15157        }
15158        try{// try catch for cross domain access issue
15159            var ssRules = stylesheet.cssRules || stylesheet.rules;
15160            for(var j = ssRules.length-1; j >= 0; --j){
15161                rules[ssRules[j].selectorText] = ssRules[j];
15162            }
15163        }catch(e){}
15164    },
15165    
15166    /**
15167     * Gets all css rules for the document
15168     * @param {Boolean} refreshCache true to refresh the internal cache
15169     * @return {Object} An object (hash) of rules indexed by selector
15170     */
15171    getRules : function(refreshCache){
15172                 if(rules == null || refreshCache){
15173                         rules = {};
15174                         var ds = doc.styleSheets;
15175                         for(var i =0, len = ds.length; i < len; i++){
15176                             try{
15177                         this.cacheStyleSheet(ds[i]);
15178                     }catch(e){} 
15179                 }
15180                 }
15181                 return rules;
15182         },
15183         
15184         /**
15185     * Gets an an individual CSS rule by selector(s)
15186     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15187     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15188     * @return {CSSRule} The CSS rule or null if one is not found
15189     */
15190    getRule : function(selector, refreshCache){
15191                 var rs = this.getRules(refreshCache);
15192                 if(!(selector instanceof Array)){
15193                     return rs[selector];
15194                 }
15195                 for(var i = 0; i < selector.length; i++){
15196                         if(rs[selector[i]]){
15197                                 return rs[selector[i]];
15198                         }
15199                 }
15200                 return null;
15201         },
15202         
15203         
15204         /**
15205     * Updates a rule property
15206     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15207     * @param {String} property The css property
15208     * @param {String} value The new value for the property
15209     * @return {Boolean} true If a rule was found and updated
15210     */
15211    updateRule : function(selector, property, value){
15212                 if(!(selector instanceof Array)){
15213                         var rule = this.getRule(selector);
15214                         if(rule){
15215                                 rule.style[property.replace(camelRe, camelFn)] = value;
15216                                 return true;
15217                         }
15218                 }else{
15219                         for(var i = 0; i < selector.length; i++){
15220                                 if(this.updateRule(selector[i], property, value)){
15221                                         return true;
15222                                 }
15223                         }
15224                 }
15225                 return false;
15226         }
15227    };   
15228 }();/*
15229  * Based on:
15230  * Ext JS Library 1.1.1
15231  * Copyright(c) 2006-2007, Ext JS, LLC.
15232  *
15233  * Originally Released Under LGPL - original licence link has changed is not relivant.
15234  *
15235  * Fork - LGPL
15236  * <script type="text/javascript">
15237  */
15238
15239  
15240
15241 /**
15242  * @class Roo.util.ClickRepeater
15243  * @extends Roo.util.Observable
15244  * 
15245  * A wrapper class which can be applied to any element. Fires a "click" event while the
15246  * mouse is pressed. The interval between firings may be specified in the config but
15247  * defaults to 10 milliseconds.
15248  * 
15249  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15250  * 
15251  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15252  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15253  * Similar to an autorepeat key delay.
15254  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15255  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15256  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15257  *           "interval" and "delay" are ignored. "immediate" is honored.
15258  * @cfg {Boolean} preventDefault True to prevent the default click event
15259  * @cfg {Boolean} stopDefault True to stop the default click event
15260  * 
15261  * @history
15262  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15263  *     2007-02-02 jvs Renamed to ClickRepeater
15264  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15265  *
15266  *  @constructor
15267  * @param {String/HTMLElement/Element} el The element to listen on
15268  * @param {Object} config
15269  **/
15270 Roo.util.ClickRepeater = function(el, config)
15271 {
15272     this.el = Roo.get(el);
15273     this.el.unselectable();
15274
15275     Roo.apply(this, config);
15276
15277     this.addEvents({
15278     /**
15279      * @event mousedown
15280      * Fires when the mouse button is depressed.
15281      * @param {Roo.util.ClickRepeater} this
15282      */
15283         "mousedown" : true,
15284     /**
15285      * @event click
15286      * Fires on a specified interval during the time the element is pressed.
15287      * @param {Roo.util.ClickRepeater} this
15288      */
15289         "click" : true,
15290     /**
15291      * @event mouseup
15292      * Fires when the mouse key is released.
15293      * @param {Roo.util.ClickRepeater} this
15294      */
15295         "mouseup" : true
15296     });
15297
15298     this.el.on("mousedown", this.handleMouseDown, this);
15299     if(this.preventDefault || this.stopDefault){
15300         this.el.on("click", function(e){
15301             if(this.preventDefault){
15302                 e.preventDefault();
15303             }
15304             if(this.stopDefault){
15305                 e.stopEvent();
15306             }
15307         }, this);
15308     }
15309
15310     // allow inline handler
15311     if(this.handler){
15312         this.on("click", this.handler,  this.scope || this);
15313     }
15314
15315     Roo.util.ClickRepeater.superclass.constructor.call(this);
15316 };
15317
15318 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15319     interval : 20,
15320     delay: 250,
15321     preventDefault : true,
15322     stopDefault : false,
15323     timer : 0,
15324
15325     // private
15326     handleMouseDown : function(){
15327         clearTimeout(this.timer);
15328         this.el.blur();
15329         if(this.pressClass){
15330             this.el.addClass(this.pressClass);
15331         }
15332         this.mousedownTime = new Date();
15333
15334         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15335         this.el.on("mouseout", this.handleMouseOut, this);
15336
15337         this.fireEvent("mousedown", this);
15338         this.fireEvent("click", this);
15339         
15340         this.timer = this.click.defer(this.delay || this.interval, this);
15341     },
15342
15343     // private
15344     click : function(){
15345         this.fireEvent("click", this);
15346         this.timer = this.click.defer(this.getInterval(), this);
15347     },
15348
15349     // private
15350     getInterval: function(){
15351         if(!this.accelerate){
15352             return this.interval;
15353         }
15354         var pressTime = this.mousedownTime.getElapsed();
15355         if(pressTime < 500){
15356             return 400;
15357         }else if(pressTime < 1700){
15358             return 320;
15359         }else if(pressTime < 2600){
15360             return 250;
15361         }else if(pressTime < 3500){
15362             return 180;
15363         }else if(pressTime < 4400){
15364             return 140;
15365         }else if(pressTime < 5300){
15366             return 80;
15367         }else if(pressTime < 6200){
15368             return 50;
15369         }else{
15370             return 10;
15371         }
15372     },
15373
15374     // private
15375     handleMouseOut : function(){
15376         clearTimeout(this.timer);
15377         if(this.pressClass){
15378             this.el.removeClass(this.pressClass);
15379         }
15380         this.el.on("mouseover", this.handleMouseReturn, this);
15381     },
15382
15383     // private
15384     handleMouseReturn : function(){
15385         this.el.un("mouseover", this.handleMouseReturn);
15386         if(this.pressClass){
15387             this.el.addClass(this.pressClass);
15388         }
15389         this.click();
15390     },
15391
15392     // private
15393     handleMouseUp : function(){
15394         clearTimeout(this.timer);
15395         this.el.un("mouseover", this.handleMouseReturn);
15396         this.el.un("mouseout", this.handleMouseOut);
15397         Roo.get(document).un("mouseup", this.handleMouseUp);
15398         this.el.removeClass(this.pressClass);
15399         this.fireEvent("mouseup", this);
15400     }
15401 });/**
15402  * @class Roo.util.Clipboard
15403  * @static
15404  * 
15405  * Clipboard UTILS
15406  * 
15407  **/
15408 Roo.util.Clipboard = {
15409     /**
15410      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15411      * @param {String} text to copy to clipboard
15412      */
15413     write : function(text) {
15414         // navigator clipboard api needs a secure context (https)
15415         if (navigator.clipboard && window.isSecureContext) {
15416             // navigator clipboard api method'
15417             navigator.clipboard.writeText(text);
15418             return ;
15419         } 
15420         // text area method
15421         var ta = document.createElement("textarea");
15422         ta.value = text;
15423         // make the textarea out of viewport
15424         ta.style.position = "fixed";
15425         ta.style.left = "-999999px";
15426         ta.style.top = "-999999px";
15427         document.body.appendChild(ta);
15428         ta.focus();
15429         ta.select();
15430         document.execCommand('copy');
15431         (function() {
15432             ta.remove();
15433         }).defer(100);
15434         
15435     }
15436         
15437 }
15438     /*
15439  * Based on:
15440  * Ext JS Library 1.1.1
15441  * Copyright(c) 2006-2007, Ext JS, LLC.
15442  *
15443  * Originally Released Under LGPL - original licence link has changed is not relivant.
15444  *
15445  * Fork - LGPL
15446  * <script type="text/javascript">
15447  */
15448
15449  
15450 /**
15451  * @class Roo.KeyNav
15452  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15453  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15454  * way to implement custom navigation schemes for any UI component.</p>
15455  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15456  * pageUp, pageDown, del, home, end.  Usage:</p>
15457  <pre><code>
15458 var nav = new Roo.KeyNav("my-element", {
15459     "left" : function(e){
15460         this.moveLeft(e.ctrlKey);
15461     },
15462     "right" : function(e){
15463         this.moveRight(e.ctrlKey);
15464     },
15465     "enter" : function(e){
15466         this.save();
15467     },
15468     scope : this
15469 });
15470 </code></pre>
15471  * @constructor
15472  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15473  * @param {Object} config The config
15474  */
15475 Roo.KeyNav = function(el, config){
15476     this.el = Roo.get(el);
15477     Roo.apply(this, config);
15478     if(!this.disabled){
15479         this.disabled = true;
15480         this.enable();
15481     }
15482 };
15483
15484 Roo.KeyNav.prototype = {
15485     /**
15486      * @cfg {Boolean} disabled
15487      * True to disable this KeyNav instance (defaults to false)
15488      */
15489     disabled : false,
15490     /**
15491      * @cfg {String} defaultEventAction
15492      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15493      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15494      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15495      */
15496     defaultEventAction: "stopEvent",
15497     /**
15498      * @cfg {Boolean} forceKeyDown
15499      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15500      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15501      * handle keydown instead of keypress.
15502      */
15503     forceKeyDown : false,
15504
15505     // private
15506     prepareEvent : function(e){
15507         var k = e.getKey();
15508         var h = this.keyToHandler[k];
15509         //if(h && this[h]){
15510         //    e.stopPropagation();
15511         //}
15512         if(Roo.isSafari && h && k >= 37 && k <= 40){
15513             e.stopEvent();
15514         }
15515     },
15516
15517     // private
15518     relay : function(e){
15519         var k = e.getKey();
15520         var h = this.keyToHandler[k];
15521         if(h && this[h]){
15522             if(this.doRelay(e, this[h], h) !== true){
15523                 e[this.defaultEventAction]();
15524             }
15525         }
15526     },
15527
15528     // private
15529     doRelay : function(e, h, hname){
15530         return h.call(this.scope || this, e);
15531     },
15532
15533     // possible handlers
15534     enter : false,
15535     left : false,
15536     right : false,
15537     up : false,
15538     down : false,
15539     tab : false,
15540     esc : false,
15541     pageUp : false,
15542     pageDown : false,
15543     del : false,
15544     home : false,
15545     end : false,
15546
15547     // quick lookup hash
15548     keyToHandler : {
15549         37 : "left",
15550         39 : "right",
15551         38 : "up",
15552         40 : "down",
15553         33 : "pageUp",
15554         34 : "pageDown",
15555         46 : "del",
15556         36 : "home",
15557         35 : "end",
15558         13 : "enter",
15559         27 : "esc",
15560         9  : "tab"
15561     },
15562
15563         /**
15564          * Enable this KeyNav
15565          */
15566         enable: function(){
15567                 if(this.disabled){
15568             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15569             // the EventObject will normalize Safari automatically
15570             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15571                 this.el.on("keydown", this.relay,  this);
15572             }else{
15573                 this.el.on("keydown", this.prepareEvent,  this);
15574                 this.el.on("keypress", this.relay,  this);
15575             }
15576                     this.disabled = false;
15577                 }
15578         },
15579
15580         /**
15581          * Disable this KeyNav
15582          */
15583         disable: function(){
15584                 if(!this.disabled){
15585                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15586                 this.el.un("keydown", this.relay);
15587             }else{
15588                 this.el.un("keydown", this.prepareEvent);
15589                 this.el.un("keypress", this.relay);
15590             }
15591                     this.disabled = true;
15592                 }
15593         }
15594 };/*
15595  * Based on:
15596  * Ext JS Library 1.1.1
15597  * Copyright(c) 2006-2007, Ext JS, LLC.
15598  *
15599  * Originally Released Under LGPL - original licence link has changed is not relivant.
15600  *
15601  * Fork - LGPL
15602  * <script type="text/javascript">
15603  */
15604
15605  
15606 /**
15607  * @class Roo.KeyMap
15608  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15609  * The constructor accepts the same config object as defined by {@link #addBinding}.
15610  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15611  * combination it will call the function with this signature (if the match is a multi-key
15612  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15613  * A KeyMap can also handle a string representation of keys.<br />
15614  * Usage:
15615  <pre><code>
15616 // map one key by key code
15617 var map = new Roo.KeyMap("my-element", {
15618     key: 13, // or Roo.EventObject.ENTER
15619     fn: myHandler,
15620     scope: myObject
15621 });
15622
15623 // map multiple keys to one action by string
15624 var map = new Roo.KeyMap("my-element", {
15625     key: "a\r\n\t",
15626     fn: myHandler,
15627     scope: myObject
15628 });
15629
15630 // map multiple keys to multiple actions by strings and array of codes
15631 var map = new Roo.KeyMap("my-element", [
15632     {
15633         key: [10,13],
15634         fn: function(){ alert("Return was pressed"); }
15635     }, {
15636         key: "abc",
15637         fn: function(){ alert('a, b or c was pressed'); }
15638     }, {
15639         key: "\t",
15640         ctrl:true,
15641         shift:true,
15642         fn: function(){ alert('Control + shift + tab was pressed.'); }
15643     }
15644 ]);
15645 </code></pre>
15646  * <b>Note: A KeyMap starts enabled</b>
15647  * @constructor
15648  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15649  * @param {Object} config The config (see {@link #addBinding})
15650  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15651  */
15652 Roo.KeyMap = function(el, config, eventName){
15653     this.el  = Roo.get(el);
15654     this.eventName = eventName || "keydown";
15655     this.bindings = [];
15656     if(config){
15657         this.addBinding(config);
15658     }
15659     this.enable();
15660 };
15661
15662 Roo.KeyMap.prototype = {
15663     /**
15664      * True to stop the event from bubbling and prevent the default browser action if the
15665      * key was handled by the KeyMap (defaults to false)
15666      * @type Boolean
15667      */
15668     stopEvent : false,
15669
15670     /**
15671      * Add a new binding to this KeyMap. The following config object properties are supported:
15672      * <pre>
15673 Property    Type             Description
15674 ----------  ---------------  ----------------------------------------------------------------------
15675 key         String/Array     A single keycode or an array of keycodes to handle
15676 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15677 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15678 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15679 fn          Function         The function to call when KeyMap finds the expected key combination
15680 scope       Object           The scope of the callback function
15681 </pre>
15682      *
15683      * Usage:
15684      * <pre><code>
15685 // Create a KeyMap
15686 var map = new Roo.KeyMap(document, {
15687     key: Roo.EventObject.ENTER,
15688     fn: handleKey,
15689     scope: this
15690 });
15691
15692 //Add a new binding to the existing KeyMap later
15693 map.addBinding({
15694     key: 'abc',
15695     shift: true,
15696     fn: handleKey,
15697     scope: this
15698 });
15699 </code></pre>
15700      * @param {Object/Array} config A single KeyMap config or an array of configs
15701      */
15702         addBinding : function(config){
15703         if(config instanceof Array){
15704             for(var i = 0, len = config.length; i < len; i++){
15705                 this.addBinding(config[i]);
15706             }
15707             return;
15708         }
15709         var keyCode = config.key,
15710             shift = config.shift, 
15711             ctrl = config.ctrl, 
15712             alt = config.alt,
15713             fn = config.fn,
15714             scope = config.scope;
15715         if(typeof keyCode == "string"){
15716             var ks = [];
15717             var keyString = keyCode.toUpperCase();
15718             for(var j = 0, len = keyString.length; j < len; j++){
15719                 ks.push(keyString.charCodeAt(j));
15720             }
15721             keyCode = ks;
15722         }
15723         var keyArray = keyCode instanceof Array;
15724         var handler = function(e){
15725             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15726                 var k = e.getKey();
15727                 if(keyArray){
15728                     for(var i = 0, len = keyCode.length; i < len; i++){
15729                         if(keyCode[i] == k){
15730                           if(this.stopEvent){
15731                               e.stopEvent();
15732                           }
15733                           fn.call(scope || window, k, e);
15734                           return;
15735                         }
15736                     }
15737                 }else{
15738                     if(k == keyCode){
15739                         if(this.stopEvent){
15740                            e.stopEvent();
15741                         }
15742                         fn.call(scope || window, k, e);
15743                     }
15744                 }
15745             }
15746         };
15747         this.bindings.push(handler);  
15748         },
15749
15750     /**
15751      * Shorthand for adding a single key listener
15752      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15753      * following options:
15754      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15755      * @param {Function} fn The function to call
15756      * @param {Object} scope (optional) The scope of the function
15757      */
15758     on : function(key, fn, scope){
15759         var keyCode, shift, ctrl, alt;
15760         if(typeof key == "object" && !(key instanceof Array)){
15761             keyCode = key.key;
15762             shift = key.shift;
15763             ctrl = key.ctrl;
15764             alt = key.alt;
15765         }else{
15766             keyCode = key;
15767         }
15768         this.addBinding({
15769             key: keyCode,
15770             shift: shift,
15771             ctrl: ctrl,
15772             alt: alt,
15773             fn: fn,
15774             scope: scope
15775         })
15776     },
15777
15778     // private
15779     handleKeyDown : function(e){
15780             if(this.enabled){ //just in case
15781             var b = this.bindings;
15782             for(var i = 0, len = b.length; i < len; i++){
15783                 b[i].call(this, e);
15784             }
15785             }
15786         },
15787         
15788         /**
15789          * Returns true if this KeyMap is enabled
15790          * @return {Boolean} 
15791          */
15792         isEnabled : function(){
15793             return this.enabled;  
15794         },
15795         
15796         /**
15797          * Enables this KeyMap
15798          */
15799         enable: function(){
15800                 if(!this.enabled){
15801                     this.el.on(this.eventName, this.handleKeyDown, this);
15802                     this.enabled = true;
15803                 }
15804         },
15805
15806         /**
15807          * Disable this KeyMap
15808          */
15809         disable: function(){
15810                 if(this.enabled){
15811                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15812                     this.enabled = false;
15813                 }
15814         }
15815 };/*
15816  * Based on:
15817  * Ext JS Library 1.1.1
15818  * Copyright(c) 2006-2007, Ext JS, LLC.
15819  *
15820  * Originally Released Under LGPL - original licence link has changed is not relivant.
15821  *
15822  * Fork - LGPL
15823  * <script type="text/javascript">
15824  */
15825
15826  
15827 /**
15828  * @class Roo.util.TextMetrics
15829  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15830  * wide, in pixels, a given block of text will be.
15831  * @static
15832  */
15833 Roo.util.TextMetrics = function(){
15834     var shared;
15835     return {
15836         /**
15837          * Measures the size of the specified text
15838          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15839          * that can affect the size of the rendered text
15840          * @param {String} text The text to measure
15841          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15842          * in order to accurately measure the text height
15843          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15844          */
15845         measure : function(el, text, fixedWidth){
15846             if(!shared){
15847                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15848             }
15849             shared.bind(el);
15850             shared.setFixedWidth(fixedWidth || 'auto');
15851             return shared.getSize(text);
15852         },
15853
15854         /**
15855          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15856          * the overhead of multiple calls to initialize the style properties on each measurement.
15857          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15858          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15859          * in order to accurately measure the text height
15860          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15861          */
15862         createInstance : function(el, fixedWidth){
15863             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15864         }
15865     };
15866 }();
15867
15868 /**
15869  * @class Roo.util.TextMetrics.Instance
15870  * Instance of  TextMetrics Calcuation
15871  * @constructor
15872  * Create a new TextMetrics Instance
15873  * @param {Object} bindto
15874  * @param {Boolean} fixedWidth
15875  */
15876
15877 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15878 {
15879     var ml = new Roo.Element(document.createElement('div'));
15880     document.body.appendChild(ml.dom);
15881     ml.position('absolute');
15882     ml.setLeftTop(-1000, -1000);
15883     ml.hide();
15884
15885     if(fixedWidth){
15886         ml.setWidth(fixedWidth);
15887     }
15888      
15889     var instance = {
15890         /**
15891          * Returns the size of the specified text based on the internal element's style and width properties
15892          * @param {String} text The text to measure
15893          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15894          */
15895         getSize : function(text){
15896             ml.update(text);
15897             var s = ml.getSize();
15898             ml.update('');
15899             return s;
15900         },
15901
15902         /**
15903          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15904          * that can affect the size of the rendered text
15905          * @param {String/HTMLElement} el The element, dom node or id
15906          */
15907         bind : function(el){
15908             ml.setStyle(
15909                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15910             );
15911         },
15912
15913         /**
15914          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15915          * to set a fixed width in order to accurately measure the text height.
15916          * @param {Number} width The width to set on the element
15917          */
15918         setFixedWidth : function(width){
15919             ml.setWidth(width);
15920         },
15921
15922         /**
15923          * Returns the measured width of the specified text
15924          * @param {String} text The text to measure
15925          * @return {Number} width The width in pixels
15926          */
15927         getWidth : function(text){
15928             ml.dom.style.width = 'auto';
15929             return this.getSize(text).width;
15930         },
15931
15932         /**
15933          * Returns the measured height of the specified text.  For multiline text, be sure to call
15934          * {@link #setFixedWidth} if necessary.
15935          * @param {String} text The text to measure
15936          * @return {Number} height The height in pixels
15937          */
15938         getHeight : function(text){
15939             return this.getSize(text).height;
15940         }
15941     };
15942
15943     instance.bind(bindTo);
15944
15945     return instance;
15946 };
15947
15948 // backwards compat
15949 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15950  * Based on:
15951  * Ext JS Library 1.1.1
15952  * Copyright(c) 2006-2007, Ext JS, LLC.
15953  *
15954  * Originally Released Under LGPL - original licence link has changed is not relivant.
15955  *
15956  * Fork - LGPL
15957  * <script type="text/javascript">
15958  */
15959
15960 /**
15961  * @class Roo.state.Provider
15962  * Abstract base class for state provider implementations. This class provides methods
15963  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15964  * Provider interface.
15965  */
15966 Roo.state.Provider = function(){
15967     /**
15968      * @event statechange
15969      * Fires when a state change occurs.
15970      * @param {Provider} this This state provider
15971      * @param {String} key The state key which was changed
15972      * @param {String} value The encoded value for the state
15973      */
15974     this.addEvents({
15975         "statechange": true
15976     });
15977     this.state = {};
15978     Roo.state.Provider.superclass.constructor.call(this);
15979 };
15980 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15981     /**
15982      * Returns the current value for a key
15983      * @param {String} name The key name
15984      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15985      * @return {Mixed} The state data
15986      */
15987     get : function(name, defaultValue){
15988         return typeof this.state[name] == "undefined" ?
15989             defaultValue : this.state[name];
15990     },
15991     
15992     /**
15993      * Clears a value from the state
15994      * @param {String} name The key name
15995      */
15996     clear : function(name){
15997         delete this.state[name];
15998         this.fireEvent("statechange", this, name, null);
15999     },
16000     
16001     /**
16002      * Sets the value for a key
16003      * @param {String} name The key name
16004      * @param {Mixed} value The value to set
16005      */
16006     set : function(name, value){
16007         this.state[name] = value;
16008         this.fireEvent("statechange", this, name, value);
16009     },
16010     
16011     /**
16012      * Decodes a string previously encoded with {@link #encodeValue}.
16013      * @param {String} value The value to decode
16014      * @return {Mixed} The decoded value
16015      */
16016     decodeValue : function(cookie){
16017         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16018         var matches = re.exec(unescape(cookie));
16019         if(!matches || !matches[1]) {
16020             return; // non state cookie
16021         }
16022         var type = matches[1];
16023         var v = matches[2];
16024         switch(type){
16025             case "n":
16026                 return parseFloat(v);
16027             case "d":
16028                 return new Date(Date.parse(v));
16029             case "b":
16030                 return (v == "1");
16031             case "a":
16032                 var all = [];
16033                 var values = v.split("^");
16034                 for(var i = 0, len = values.length; i < len; i++){
16035                     all.push(this.decodeValue(values[i]));
16036                 }
16037                 return all;
16038            case "o":
16039                 var all = {};
16040                 var values = v.split("^");
16041                 for(var i = 0, len = values.length; i < len; i++){
16042                     var kv = values[i].split("=");
16043                     all[kv[0]] = this.decodeValue(kv[1]);
16044                 }
16045                 return all;
16046            default:
16047                 return v;
16048         }
16049     },
16050     
16051     /**
16052      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16053      * @param {Mixed} value The value to encode
16054      * @return {String} The encoded value
16055      */
16056     encodeValue : function(v){
16057         var enc;
16058         if(typeof v == "number"){
16059             enc = "n:" + v;
16060         }else if(typeof v == "boolean"){
16061             enc = "b:" + (v ? "1" : "0");
16062         }else if(v instanceof Date){
16063             enc = "d:" + v.toGMTString();
16064         }else if(v instanceof Array){
16065             var flat = "";
16066             for(var i = 0, len = v.length; i < len; i++){
16067                 flat += this.encodeValue(v[i]);
16068                 if(i != len-1) {
16069                     flat += "^";
16070                 }
16071             }
16072             enc = "a:" + flat;
16073         }else if(typeof v == "object"){
16074             var flat = "";
16075             for(var key in v){
16076                 if(typeof v[key] != "function"){
16077                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16078                 }
16079             }
16080             enc = "o:" + flat.substring(0, flat.length-1);
16081         }else{
16082             enc = "s:" + v;
16083         }
16084         return escape(enc);        
16085     }
16086 });
16087
16088 /*
16089  * Based on:
16090  * Ext JS Library 1.1.1
16091  * Copyright(c) 2006-2007, Ext JS, LLC.
16092  *
16093  * Originally Released Under LGPL - original licence link has changed is not relivant.
16094  *
16095  * Fork - LGPL
16096  * <script type="text/javascript">
16097  */
16098 /**
16099  * @class Roo.state.Manager
16100  * This is the global state manager. By default all components that are "state aware" check this class
16101  * for state information if you don't pass them a custom state provider. In order for this class
16102  * to be useful, it must be initialized with a provider when your application initializes.
16103  <pre><code>
16104 // in your initialization function
16105 init : function(){
16106    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16107    ...
16108    // supposed you have a {@link Roo.BorderLayout}
16109    var layout = new Roo.BorderLayout(...);
16110    layout.restoreState();
16111    // or a {Roo.BasicDialog}
16112    var dialog = new Roo.BasicDialog(...);
16113    dialog.restoreState();
16114  </code></pre>
16115  * @static
16116  */
16117 Roo.state.Manager = function(){
16118     var provider = new Roo.state.Provider();
16119     
16120     return {
16121         /**
16122          * Configures the default state provider for your application
16123          * @param {Provider} stateProvider The state provider to set
16124          */
16125         setProvider : function(stateProvider){
16126             provider = stateProvider;
16127         },
16128         
16129         /**
16130          * Returns the current value for a key
16131          * @param {String} name The key name
16132          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16133          * @return {Mixed} The state data
16134          */
16135         get : function(key, defaultValue){
16136             return provider.get(key, defaultValue);
16137         },
16138         
16139         /**
16140          * Sets the value for a key
16141          * @param {String} name The key name
16142          * @param {Mixed} value The state data
16143          */
16144          set : function(key, value){
16145             provider.set(key, value);
16146         },
16147         
16148         /**
16149          * Clears a value from the state
16150          * @param {String} name The key name
16151          */
16152         clear : function(key){
16153             provider.clear(key);
16154         },
16155         
16156         /**
16157          * Gets the currently configured state provider
16158          * @return {Provider} The state provider
16159          */
16160         getProvider : function(){
16161             return provider;
16162         }
16163     };
16164 }();
16165 /*
16166  * Based on:
16167  * Ext JS Library 1.1.1
16168  * Copyright(c) 2006-2007, Ext JS, LLC.
16169  *
16170  * Originally Released Under LGPL - original licence link has changed is not relivant.
16171  *
16172  * Fork - LGPL
16173  * <script type="text/javascript">
16174  */
16175 /**
16176  * @class Roo.state.CookieProvider
16177  * @extends Roo.state.Provider
16178  * The default Provider implementation which saves state via cookies.
16179  * <br />Usage:
16180  <pre><code>
16181    var cp = new Roo.state.CookieProvider({
16182        path: "/cgi-bin/",
16183        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16184        domain: "roojs.com"
16185    })
16186    Roo.state.Manager.setProvider(cp);
16187  </code></pre>
16188  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16189  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16190  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16191  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16192  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16193  * domain the page is running on including the 'www' like 'www.roojs.com')
16194  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16195  * @constructor
16196  * Create a new CookieProvider
16197  * @param {Object} config The configuration object
16198  */
16199 Roo.state.CookieProvider = function(config){
16200     Roo.state.CookieProvider.superclass.constructor.call(this);
16201     this.path = "/";
16202     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16203     this.domain = null;
16204     this.secure = false;
16205     Roo.apply(this, config);
16206     this.state = this.readCookies();
16207 };
16208
16209 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16210     // private
16211     set : function(name, value){
16212         if(typeof value == "undefined" || value === null){
16213             this.clear(name);
16214             return;
16215         }
16216         this.setCookie(name, value);
16217         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16218     },
16219
16220     // private
16221     clear : function(name){
16222         this.clearCookie(name);
16223         Roo.state.CookieProvider.superclass.clear.call(this, name);
16224     },
16225
16226     // private
16227     readCookies : function(){
16228         var cookies = {};
16229         var c = document.cookie + ";";
16230         var re = /\s?(.*?)=(.*?);/g;
16231         var matches;
16232         while((matches = re.exec(c)) != null){
16233             var name = matches[1];
16234             var value = matches[2];
16235             if(name && name.substring(0,3) == "ys-"){
16236                 cookies[name.substr(3)] = this.decodeValue(value);
16237             }
16238         }
16239         return cookies;
16240     },
16241
16242     // private
16243     setCookie : function(name, value){
16244         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16245            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16246            ((this.path == null) ? "" : ("; path=" + this.path)) +
16247            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16248            ((this.secure == true) ? "; secure" : "");
16249     },
16250
16251     // private
16252     clearCookie : function(name){
16253         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16254            ((this.path == null) ? "" : ("; path=" + this.path)) +
16255            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16256            ((this.secure == true) ? "; secure" : "");
16257     }
16258 });/*
16259  * Based on:
16260  * Ext JS Library 1.1.1
16261  * Copyright(c) 2006-2007, Ext JS, LLC.
16262  *
16263  * Originally Released Under LGPL - original licence link has changed is not relivant.
16264  *
16265  * Fork - LGPL
16266  * <script type="text/javascript">
16267  */
16268  
16269
16270 /**
16271  * @class Roo.ComponentMgr
16272  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16273  * @static
16274  */
16275 Roo.ComponentMgr = function(){
16276     var all = new Roo.util.MixedCollection();
16277
16278     return {
16279         /**
16280          * Registers a component.
16281          * @param {Roo.Component} c The component
16282          */
16283         register : function(c){
16284             all.add(c);
16285         },
16286
16287         /**
16288          * Unregisters a component.
16289          * @param {Roo.Component} c The component
16290          */
16291         unregister : function(c){
16292             all.remove(c);
16293         },
16294
16295         /**
16296          * Returns a component by id
16297          * @param {String} id The component id
16298          */
16299         get : function(id){
16300             return all.get(id);
16301         },
16302
16303         /**
16304          * Registers a function that will be called when a specified component is added to ComponentMgr
16305          * @param {String} id The component id
16306          * @param {Funtction} fn The callback function
16307          * @param {Object} scope The scope of the callback
16308          */
16309         onAvailable : function(id, fn, scope){
16310             all.on("add", function(index, o){
16311                 if(o.id == id){
16312                     fn.call(scope || o, o);
16313                     all.un("add", fn, scope);
16314                 }
16315             });
16316         }
16317     };
16318 }();/*
16319  * Based on:
16320  * Ext JS Library 1.1.1
16321  * Copyright(c) 2006-2007, Ext JS, LLC.
16322  *
16323  * Originally Released Under LGPL - original licence link has changed is not relivant.
16324  *
16325  * Fork - LGPL
16326  * <script type="text/javascript">
16327  */
16328  
16329 /**
16330  * @class Roo.Component
16331  * @extends Roo.util.Observable
16332  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16333  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16334  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16335  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16336  * All visual components (widgets) that require rendering into a layout should subclass Component.
16337  * @constructor
16338  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16339  * 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
16340  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16341  */
16342 Roo.Component = function(config){
16343     config = config || {};
16344     if(config.tagName || config.dom || typeof config == "string"){ // element object
16345         config = {el: config, id: config.id || config};
16346     }
16347     this.initialConfig = config;
16348
16349     Roo.apply(this, config);
16350     this.addEvents({
16351         /**
16352          * @event disable
16353          * Fires after the component is disabled.
16354              * @param {Roo.Component} this
16355              */
16356         disable : true,
16357         /**
16358          * @event enable
16359          * Fires after the component is enabled.
16360              * @param {Roo.Component} this
16361              */
16362         enable : true,
16363         /**
16364          * @event beforeshow
16365          * Fires before the component is shown.  Return false to stop the show.
16366              * @param {Roo.Component} this
16367              */
16368         beforeshow : true,
16369         /**
16370          * @event show
16371          * Fires after the component is shown.
16372              * @param {Roo.Component} this
16373              */
16374         show : true,
16375         /**
16376          * @event beforehide
16377          * Fires before the component is hidden. Return false to stop the hide.
16378              * @param {Roo.Component} this
16379              */
16380         beforehide : true,
16381         /**
16382          * @event hide
16383          * Fires after the component is hidden.
16384              * @param {Roo.Component} this
16385              */
16386         hide : true,
16387         /**
16388          * @event beforerender
16389          * Fires before the component is rendered. Return false to stop the render.
16390              * @param {Roo.Component} this
16391              */
16392         beforerender : true,
16393         /**
16394          * @event render
16395          * Fires after the component is rendered.
16396              * @param {Roo.Component} this
16397              */
16398         render : true,
16399         /**
16400          * @event beforedestroy
16401          * Fires before the component is destroyed. Return false to stop the destroy.
16402              * @param {Roo.Component} this
16403              */
16404         beforedestroy : true,
16405         /**
16406          * @event destroy
16407          * Fires after the component is destroyed.
16408              * @param {Roo.Component} this
16409              */
16410         destroy : true
16411     });
16412     if(!this.id){
16413         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16414     }
16415     Roo.ComponentMgr.register(this);
16416     Roo.Component.superclass.constructor.call(this);
16417     this.initComponent();
16418     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16419         this.render(this.renderTo);
16420         delete this.renderTo;
16421     }
16422 };
16423
16424 /** @private */
16425 Roo.Component.AUTO_ID = 1000;
16426
16427 Roo.extend(Roo.Component, Roo.util.Observable, {
16428     /**
16429      * @scope Roo.Component.prototype
16430      * @type {Boolean}
16431      * true if this component is hidden. Read-only.
16432      */
16433     hidden : false,
16434     /**
16435      * @type {Boolean}
16436      * true if this component is disabled. Read-only.
16437      */
16438     disabled : false,
16439     /**
16440      * @type {Boolean}
16441      * true if this component has been rendered. Read-only.
16442      */
16443     rendered : false,
16444     
16445     /** @cfg {String} disableClass
16446      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16447      */
16448     disabledClass : "x-item-disabled",
16449         /** @cfg {Boolean} allowDomMove
16450          * Whether the component can move the Dom node when rendering (defaults to true).
16451          */
16452     allowDomMove : true,
16453     /** @cfg {String} hideMode (display|visibility)
16454      * How this component should hidden. Supported values are
16455      * "visibility" (css visibility), "offsets" (negative offset position) and
16456      * "display" (css display) - defaults to "display".
16457      */
16458     hideMode: 'display',
16459
16460     /** @private */
16461     ctype : "Roo.Component",
16462
16463     /**
16464      * @cfg {String} actionMode 
16465      * which property holds the element that used for  hide() / show() / disable() / enable()
16466      * default is 'el' for forms you probably want to set this to fieldEl 
16467      */
16468     actionMode : "el",
16469
16470     /** @private */
16471     getActionEl : function(){
16472         return this[this.actionMode];
16473     },
16474
16475     initComponent : Roo.emptyFn,
16476     /**
16477      * If this is a lazy rendering component, render it to its container element.
16478      * @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.
16479      */
16480     render : function(container, position){
16481         
16482         if(this.rendered){
16483             return this;
16484         }
16485         
16486         if(this.fireEvent("beforerender", this) === false){
16487             return false;
16488         }
16489         
16490         if(!container && this.el){
16491             this.el = Roo.get(this.el);
16492             container = this.el.dom.parentNode;
16493             this.allowDomMove = false;
16494         }
16495         this.container = Roo.get(container);
16496         this.rendered = true;
16497         if(position !== undefined){
16498             if(typeof position == 'number'){
16499                 position = this.container.dom.childNodes[position];
16500             }else{
16501                 position = Roo.getDom(position);
16502             }
16503         }
16504         this.onRender(this.container, position || null);
16505         if(this.cls){
16506             this.el.addClass(this.cls);
16507             delete this.cls;
16508         }
16509         if(this.style){
16510             this.el.applyStyles(this.style);
16511             delete this.style;
16512         }
16513         this.fireEvent("render", this);
16514         this.afterRender(this.container);
16515         if(this.hidden){
16516             this.hide();
16517         }
16518         if(this.disabled){
16519             this.disable();
16520         }
16521
16522         return this;
16523         
16524     },
16525
16526     /** @private */
16527     // default function is not really useful
16528     onRender : function(ct, position){
16529         if(this.el){
16530             this.el = Roo.get(this.el);
16531             if(this.allowDomMove !== false){
16532                 ct.dom.insertBefore(this.el.dom, position);
16533             }
16534         }
16535     },
16536
16537     /** @private */
16538     getAutoCreate : function(){
16539         var cfg = typeof this.autoCreate == "object" ?
16540                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16541         if(this.id && !cfg.id){
16542             cfg.id = this.id;
16543         }
16544         return cfg;
16545     },
16546
16547     /** @private */
16548     afterRender : Roo.emptyFn,
16549
16550     /**
16551      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16552      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16553      */
16554     destroy : function(){
16555         if(this.fireEvent("beforedestroy", this) !== false){
16556             this.purgeListeners();
16557             this.beforeDestroy();
16558             if(this.rendered){
16559                 this.el.removeAllListeners();
16560                 this.el.remove();
16561                 if(this.actionMode == "container"){
16562                     this.container.remove();
16563                 }
16564             }
16565             this.onDestroy();
16566             Roo.ComponentMgr.unregister(this);
16567             this.fireEvent("destroy", this);
16568         }
16569     },
16570
16571         /** @private */
16572     beforeDestroy : function(){
16573
16574     },
16575
16576         /** @private */
16577         onDestroy : function(){
16578
16579     },
16580
16581     /**
16582      * Returns the underlying {@link Roo.Element}.
16583      * @return {Roo.Element} The element
16584      */
16585     getEl : function(){
16586         return this.el;
16587     },
16588
16589     /**
16590      * Returns the id of this component.
16591      * @return {String}
16592      */
16593     getId : function(){
16594         return this.id;
16595     },
16596
16597     /**
16598      * Try to focus this component.
16599      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16600      * @return {Roo.Component} this
16601      */
16602     focus : function(selectText){
16603         if(this.rendered){
16604             this.el.focus();
16605             if(selectText === true){
16606                 this.el.dom.select();
16607             }
16608         }
16609         return this;
16610     },
16611
16612     /** @private */
16613     blur : function(){
16614         if(this.rendered){
16615             this.el.blur();
16616         }
16617         return this;
16618     },
16619
16620     /**
16621      * Disable this component.
16622      * @return {Roo.Component} this
16623      */
16624     disable : function(){
16625         if(this.rendered){
16626             this.onDisable();
16627         }
16628         this.disabled = true;
16629         this.fireEvent("disable", this);
16630         return this;
16631     },
16632
16633         // private
16634     onDisable : function(){
16635         this.getActionEl().addClass(this.disabledClass);
16636         this.el.dom.disabled = true;
16637     },
16638
16639     /**
16640      * Enable this component.
16641      * @return {Roo.Component} this
16642      */
16643     enable : function(){
16644         if(this.rendered){
16645             this.onEnable();
16646         }
16647         this.disabled = false;
16648         this.fireEvent("enable", this);
16649         return this;
16650     },
16651
16652         // private
16653     onEnable : function(){
16654         this.getActionEl().removeClass(this.disabledClass);
16655         this.el.dom.disabled = false;
16656     },
16657
16658     /**
16659      * Convenience function for setting disabled/enabled by boolean.
16660      * @param {Boolean} disabled
16661      */
16662     setDisabled : function(disabled){
16663         this[disabled ? "disable" : "enable"]();
16664     },
16665
16666     /**
16667      * Show this component.
16668      * @return {Roo.Component} this
16669      */
16670     show: function(){
16671         if(this.fireEvent("beforeshow", this) !== false){
16672             this.hidden = false;
16673             if(this.rendered){
16674                 this.onShow();
16675             }
16676             this.fireEvent("show", this);
16677         }
16678         return this;
16679     },
16680
16681     // private
16682     onShow : function(){
16683         var ae = this.getActionEl();
16684         if(this.hideMode == 'visibility'){
16685             ae.dom.style.visibility = "visible";
16686         }else if(this.hideMode == 'offsets'){
16687             ae.removeClass('x-hidden');
16688         }else{
16689             ae.dom.style.display = "";
16690         }
16691     },
16692
16693     /**
16694      * Hide this component.
16695      * @return {Roo.Component} this
16696      */
16697     hide: function(){
16698         if(this.fireEvent("beforehide", this) !== false){
16699             this.hidden = true;
16700             if(this.rendered){
16701                 this.onHide();
16702             }
16703             this.fireEvent("hide", this);
16704         }
16705         return this;
16706     },
16707
16708     // private
16709     onHide : function(){
16710         var ae = this.getActionEl();
16711         if(this.hideMode == 'visibility'){
16712             ae.dom.style.visibility = "hidden";
16713         }else if(this.hideMode == 'offsets'){
16714             ae.addClass('x-hidden');
16715         }else{
16716             ae.dom.style.display = "none";
16717         }
16718     },
16719
16720     /**
16721      * Convenience function to hide or show this component by boolean.
16722      * @param {Boolean} visible True to show, false to hide
16723      * @return {Roo.Component} this
16724      */
16725     setVisible: function(visible){
16726         if(visible) {
16727             this.show();
16728         }else{
16729             this.hide();
16730         }
16731         return this;
16732     },
16733
16734     /**
16735      * Returns true if this component is visible.
16736      */
16737     isVisible : function(){
16738         return this.getActionEl().isVisible();
16739     },
16740
16741     cloneConfig : function(overrides){
16742         overrides = overrides || {};
16743         var id = overrides.id || Roo.id();
16744         var cfg = Roo.applyIf(overrides, this.initialConfig);
16745         cfg.id = id; // prevent dup id
16746         return new this.constructor(cfg);
16747     }
16748 });/*
16749  * Based on:
16750  * Ext JS Library 1.1.1
16751  * Copyright(c) 2006-2007, Ext JS, LLC.
16752  *
16753  * Originally Released Under LGPL - original licence link has changed is not relivant.
16754  *
16755  * Fork - LGPL
16756  * <script type="text/javascript">
16757  */
16758
16759 /**
16760  * @class Roo.BoxComponent
16761  * @extends Roo.Component
16762  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16763  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16764  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16765  * layout containers.
16766  * @constructor
16767  * @param {Roo.Element/String/Object} config The configuration options.
16768  */
16769 Roo.BoxComponent = function(config){
16770     Roo.Component.call(this, config);
16771     this.addEvents({
16772         /**
16773          * @event resize
16774          * Fires after the component is resized.
16775              * @param {Roo.Component} this
16776              * @param {Number} adjWidth The box-adjusted width that was set
16777              * @param {Number} adjHeight The box-adjusted height that was set
16778              * @param {Number} rawWidth The width that was originally specified
16779              * @param {Number} rawHeight The height that was originally specified
16780              */
16781         resize : true,
16782         /**
16783          * @event move
16784          * Fires after the component is moved.
16785              * @param {Roo.Component} this
16786              * @param {Number} x The new x position
16787              * @param {Number} y The new y position
16788              */
16789         move : true
16790     });
16791 };
16792
16793 Roo.extend(Roo.BoxComponent, Roo.Component, {
16794     // private, set in afterRender to signify that the component has been rendered
16795     boxReady : false,
16796     // private, used to defer height settings to subclasses
16797     deferHeight: false,
16798     /** @cfg {Number} width
16799      * width (optional) size of component
16800      */
16801      /** @cfg {Number} height
16802      * height (optional) size of component
16803      */
16804      
16805     /**
16806      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16807      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16808      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16809      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16810      * @return {Roo.BoxComponent} this
16811      */
16812     setSize : function(w, h){
16813         // support for standard size objects
16814         if(typeof w == 'object'){
16815             h = w.height;
16816             w = w.width;
16817         }
16818         // not rendered
16819         if(!this.boxReady){
16820             this.width = w;
16821             this.height = h;
16822             return this;
16823         }
16824
16825         // prevent recalcs when not needed
16826         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16827             return this;
16828         }
16829         this.lastSize = {width: w, height: h};
16830
16831         var adj = this.adjustSize(w, h);
16832         var aw = adj.width, ah = adj.height;
16833         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16834             var rz = this.getResizeEl();
16835             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16836                 rz.setSize(aw, ah);
16837             }else if(!this.deferHeight && ah !== undefined){
16838                 rz.setHeight(ah);
16839             }else if(aw !== undefined){
16840                 rz.setWidth(aw);
16841             }
16842             this.onResize(aw, ah, w, h);
16843             this.fireEvent('resize', this, aw, ah, w, h);
16844         }
16845         return this;
16846     },
16847
16848     /**
16849      * Gets the current size of the component's underlying element.
16850      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16851      */
16852     getSize : function(){
16853         return this.el.getSize();
16854     },
16855
16856     /**
16857      * Gets the current XY position of the component's underlying element.
16858      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16859      * @return {Array} The XY position of the element (e.g., [100, 200])
16860      */
16861     getPosition : function(local){
16862         if(local === true){
16863             return [this.el.getLeft(true), this.el.getTop(true)];
16864         }
16865         return this.xy || this.el.getXY();
16866     },
16867
16868     /**
16869      * Gets the current box measurements of the component's underlying element.
16870      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16871      * @returns {Object} box An object in the format {x, y, width, height}
16872      */
16873     getBox : function(local){
16874         var s = this.el.getSize();
16875         if(local){
16876             s.x = this.el.getLeft(true);
16877             s.y = this.el.getTop(true);
16878         }else{
16879             var xy = this.xy || this.el.getXY();
16880             s.x = xy[0];
16881             s.y = xy[1];
16882         }
16883         return s;
16884     },
16885
16886     /**
16887      * Sets the current box measurements of the component's underlying element.
16888      * @param {Object} box An object in the format {x, y, width, height}
16889      * @returns {Roo.BoxComponent} this
16890      */
16891     updateBox : function(box){
16892         this.setSize(box.width, box.height);
16893         this.setPagePosition(box.x, box.y);
16894         return this;
16895     },
16896
16897     // protected
16898     getResizeEl : function(){
16899         return this.resizeEl || this.el;
16900     },
16901
16902     // protected
16903     getPositionEl : function(){
16904         return this.positionEl || this.el;
16905     },
16906
16907     /**
16908      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16909      * This method fires the move event.
16910      * @param {Number} left The new left
16911      * @param {Number} top The new top
16912      * @returns {Roo.BoxComponent} this
16913      */
16914     setPosition : function(x, y){
16915         this.x = x;
16916         this.y = y;
16917         if(!this.boxReady){
16918             return this;
16919         }
16920         var adj = this.adjustPosition(x, y);
16921         var ax = adj.x, ay = adj.y;
16922
16923         var el = this.getPositionEl();
16924         if(ax !== undefined || ay !== undefined){
16925             if(ax !== undefined && ay !== undefined){
16926                 el.setLeftTop(ax, ay);
16927             }else if(ax !== undefined){
16928                 el.setLeft(ax);
16929             }else if(ay !== undefined){
16930                 el.setTop(ay);
16931             }
16932             this.onPosition(ax, ay);
16933             this.fireEvent('move', this, ax, ay);
16934         }
16935         return this;
16936     },
16937
16938     /**
16939      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16940      * This method fires the move event.
16941      * @param {Number} x The new x position
16942      * @param {Number} y The new y position
16943      * @returns {Roo.BoxComponent} this
16944      */
16945     setPagePosition : function(x, y){
16946         this.pageX = x;
16947         this.pageY = y;
16948         if(!this.boxReady){
16949             return;
16950         }
16951         if(x === undefined || y === undefined){ // cannot translate undefined points
16952             return;
16953         }
16954         var p = this.el.translatePoints(x, y);
16955         this.setPosition(p.left, p.top);
16956         return this;
16957     },
16958
16959     // private
16960     onRender : function(ct, position){
16961         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16962         if(this.resizeEl){
16963             this.resizeEl = Roo.get(this.resizeEl);
16964         }
16965         if(this.positionEl){
16966             this.positionEl = Roo.get(this.positionEl);
16967         }
16968     },
16969
16970     // private
16971     afterRender : function(){
16972         Roo.BoxComponent.superclass.afterRender.call(this);
16973         this.boxReady = true;
16974         this.setSize(this.width, this.height);
16975         if(this.x || this.y){
16976             this.setPosition(this.x, this.y);
16977         }
16978         if(this.pageX || this.pageY){
16979             this.setPagePosition(this.pageX, this.pageY);
16980         }
16981     },
16982
16983     /**
16984      * Force the component's size to recalculate based on the underlying element's current height and width.
16985      * @returns {Roo.BoxComponent} this
16986      */
16987     syncSize : function(){
16988         delete this.lastSize;
16989         this.setSize(this.el.getWidth(), this.el.getHeight());
16990         return this;
16991     },
16992
16993     /**
16994      * Called after the component is resized, this method is empty by default but can be implemented by any
16995      * subclass that needs to perform custom logic after a resize occurs.
16996      * @param {Number} adjWidth The box-adjusted width that was set
16997      * @param {Number} adjHeight The box-adjusted height that was set
16998      * @param {Number} rawWidth The width that was originally specified
16999      * @param {Number} rawHeight The height that was originally specified
17000      */
17001     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17002
17003     },
17004
17005     /**
17006      * Called after the component is moved, this method is empty by default but can be implemented by any
17007      * subclass that needs to perform custom logic after a move occurs.
17008      * @param {Number} x The new x position
17009      * @param {Number} y The new y position
17010      */
17011     onPosition : function(x, y){
17012
17013     },
17014
17015     // private
17016     adjustSize : function(w, h){
17017         if(this.autoWidth){
17018             w = 'auto';
17019         }
17020         if(this.autoHeight){
17021             h = 'auto';
17022         }
17023         return {width : w, height: h};
17024     },
17025
17026     // private
17027     adjustPosition : function(x, y){
17028         return {x : x, y: y};
17029     }
17030 });/*
17031  * Based on:
17032  * Ext JS Library 1.1.1
17033  * Copyright(c) 2006-2007, Ext JS, LLC.
17034  *
17035  * Originally Released Under LGPL - original licence link has changed is not relivant.
17036  *
17037  * Fork - LGPL
17038  * <script type="text/javascript">
17039  */
17040  (function(){ 
17041 /**
17042  * @class Roo.Layer
17043  * @extends Roo.Element
17044  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17045  * automatic maintaining of shadow/shim positions.
17046  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17047  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17048  * you can pass a string with a CSS class name. False turns off the shadow.
17049  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17050  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17051  * @cfg {String} cls CSS class to add to the element
17052  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17053  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17054  * @constructor
17055  * @param {Object} config An object with config options.
17056  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17057  */
17058
17059 Roo.Layer = function(config, existingEl){
17060     config = config || {};
17061     var dh = Roo.DomHelper;
17062     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17063     if(existingEl){
17064         this.dom = Roo.getDom(existingEl);
17065     }
17066     if(!this.dom){
17067         var o = config.dh || {tag: "div", cls: "x-layer"};
17068         this.dom = dh.append(pel, o);
17069     }
17070     if(config.cls){
17071         this.addClass(config.cls);
17072     }
17073     this.constrain = config.constrain !== false;
17074     this.visibilityMode = Roo.Element.VISIBILITY;
17075     if(config.id){
17076         this.id = this.dom.id = config.id;
17077     }else{
17078         this.id = Roo.id(this.dom);
17079     }
17080     this.zindex = config.zindex || this.getZIndex();
17081     this.position("absolute", this.zindex);
17082     if(config.shadow){
17083         this.shadowOffset = config.shadowOffset || 4;
17084         this.shadow = new Roo.Shadow({
17085             offset : this.shadowOffset,
17086             mode : config.shadow
17087         });
17088     }else{
17089         this.shadowOffset = 0;
17090     }
17091     this.useShim = config.shim !== false && Roo.useShims;
17092     this.useDisplay = config.useDisplay;
17093     this.hide();
17094 };
17095
17096 var supr = Roo.Element.prototype;
17097
17098 // shims are shared among layer to keep from having 100 iframes
17099 var shims = [];
17100
17101 Roo.extend(Roo.Layer, Roo.Element, {
17102
17103     getZIndex : function(){
17104         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17105     },
17106
17107     getShim : function(){
17108         if(!this.useShim){
17109             return null;
17110         }
17111         if(this.shim){
17112             return this.shim;
17113         }
17114         var shim = shims.shift();
17115         if(!shim){
17116             shim = this.createShim();
17117             shim.enableDisplayMode('block');
17118             shim.dom.style.display = 'none';
17119             shim.dom.style.visibility = 'visible';
17120         }
17121         var pn = this.dom.parentNode;
17122         if(shim.dom.parentNode != pn){
17123             pn.insertBefore(shim.dom, this.dom);
17124         }
17125         shim.setStyle('z-index', this.getZIndex()-2);
17126         this.shim = shim;
17127         return shim;
17128     },
17129
17130     hideShim : function(){
17131         if(this.shim){
17132             this.shim.setDisplayed(false);
17133             shims.push(this.shim);
17134             delete this.shim;
17135         }
17136     },
17137
17138     disableShadow : function(){
17139         if(this.shadow){
17140             this.shadowDisabled = true;
17141             this.shadow.hide();
17142             this.lastShadowOffset = this.shadowOffset;
17143             this.shadowOffset = 0;
17144         }
17145     },
17146
17147     enableShadow : function(show){
17148         if(this.shadow){
17149             this.shadowDisabled = false;
17150             this.shadowOffset = this.lastShadowOffset;
17151             delete this.lastShadowOffset;
17152             if(show){
17153                 this.sync(true);
17154             }
17155         }
17156     },
17157
17158     // private
17159     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17160     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17161     sync : function(doShow){
17162         var sw = this.shadow;
17163         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17164             var sh = this.getShim();
17165
17166             var w = this.getWidth(),
17167                 h = this.getHeight();
17168
17169             var l = this.getLeft(true),
17170                 t = this.getTop(true);
17171
17172             if(sw && !this.shadowDisabled){
17173                 if(doShow && !sw.isVisible()){
17174                     sw.show(this);
17175                 }else{
17176                     sw.realign(l, t, w, h);
17177                 }
17178                 if(sh){
17179                     if(doShow){
17180                        sh.show();
17181                     }
17182                     // fit the shim behind the shadow, so it is shimmed too
17183                     var a = sw.adjusts, s = sh.dom.style;
17184                     s.left = (Math.min(l, l+a.l))+"px";
17185                     s.top = (Math.min(t, t+a.t))+"px";
17186                     s.width = (w+a.w)+"px";
17187                     s.height = (h+a.h)+"px";
17188                 }
17189             }else if(sh){
17190                 if(doShow){
17191                    sh.show();
17192                 }
17193                 sh.setSize(w, h);
17194                 sh.setLeftTop(l, t);
17195             }
17196             
17197         }
17198     },
17199
17200     // private
17201     destroy : function(){
17202         this.hideShim();
17203         if(this.shadow){
17204             this.shadow.hide();
17205         }
17206         this.removeAllListeners();
17207         var pn = this.dom.parentNode;
17208         if(pn){
17209             pn.removeChild(this.dom);
17210         }
17211         Roo.Element.uncache(this.id);
17212     },
17213
17214     remove : function(){
17215         this.destroy();
17216     },
17217
17218     // private
17219     beginUpdate : function(){
17220         this.updating = true;
17221     },
17222
17223     // private
17224     endUpdate : function(){
17225         this.updating = false;
17226         this.sync(true);
17227     },
17228
17229     // private
17230     hideUnders : function(negOffset){
17231         if(this.shadow){
17232             this.shadow.hide();
17233         }
17234         this.hideShim();
17235     },
17236
17237     // private
17238     constrainXY : function(){
17239         if(this.constrain){
17240             var vw = Roo.lib.Dom.getViewWidth(),
17241                 vh = Roo.lib.Dom.getViewHeight();
17242             var s = Roo.get(document).getScroll();
17243
17244             var xy = this.getXY();
17245             var x = xy[0], y = xy[1];   
17246             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17247             // only move it if it needs it
17248             var moved = false;
17249             // first validate right/bottom
17250             if((x + w) > vw+s.left){
17251                 x = vw - w - this.shadowOffset;
17252                 moved = true;
17253             }
17254             if((y + h) > vh+s.top){
17255                 y = vh - h - this.shadowOffset;
17256                 moved = true;
17257             }
17258             // then make sure top/left isn't negative
17259             if(x < s.left){
17260                 x = s.left;
17261                 moved = true;
17262             }
17263             if(y < s.top){
17264                 y = s.top;
17265                 moved = true;
17266             }
17267             if(moved){
17268                 if(this.avoidY){
17269                     var ay = this.avoidY;
17270                     if(y <= ay && (y+h) >= ay){
17271                         y = ay-h-5;   
17272                     }
17273                 }
17274                 xy = [x, y];
17275                 this.storeXY(xy);
17276                 supr.setXY.call(this, xy);
17277                 this.sync();
17278             }
17279         }
17280     },
17281
17282     isVisible : function(){
17283         return this.visible;    
17284     },
17285
17286     // private
17287     showAction : function(){
17288         this.visible = true; // track visibility to prevent getStyle calls
17289         if(this.useDisplay === true){
17290             this.setDisplayed("");
17291         }else if(this.lastXY){
17292             supr.setXY.call(this, this.lastXY);
17293         }else if(this.lastLT){
17294             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17295         }
17296     },
17297
17298     // private
17299     hideAction : function(){
17300         this.visible = false;
17301         if(this.useDisplay === true){
17302             this.setDisplayed(false);
17303         }else{
17304             this.setLeftTop(-10000,-10000);
17305         }
17306     },
17307
17308     // overridden Element method
17309     setVisible : function(v, a, d, c, e){
17310         if(v){
17311             this.showAction();
17312         }
17313         if(a && v){
17314             var cb = function(){
17315                 this.sync(true);
17316                 if(c){
17317                     c();
17318                 }
17319             }.createDelegate(this);
17320             supr.setVisible.call(this, true, true, d, cb, e);
17321         }else{
17322             if(!v){
17323                 this.hideUnders(true);
17324             }
17325             var cb = c;
17326             if(a){
17327                 cb = function(){
17328                     this.hideAction();
17329                     if(c){
17330                         c();
17331                     }
17332                 }.createDelegate(this);
17333             }
17334             supr.setVisible.call(this, v, a, d, cb, e);
17335             if(v){
17336                 this.sync(true);
17337             }else if(!a){
17338                 this.hideAction();
17339             }
17340         }
17341     },
17342
17343     storeXY : function(xy){
17344         delete this.lastLT;
17345         this.lastXY = xy;
17346     },
17347
17348     storeLeftTop : function(left, top){
17349         delete this.lastXY;
17350         this.lastLT = [left, top];
17351     },
17352
17353     // private
17354     beforeFx : function(){
17355         this.beforeAction();
17356         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17357     },
17358
17359     // private
17360     afterFx : function(){
17361         Roo.Layer.superclass.afterFx.apply(this, arguments);
17362         this.sync(this.isVisible());
17363     },
17364
17365     // private
17366     beforeAction : function(){
17367         if(!this.updating && this.shadow){
17368             this.shadow.hide();
17369         }
17370     },
17371
17372     // overridden Element method
17373     setLeft : function(left){
17374         this.storeLeftTop(left, this.getTop(true));
17375         supr.setLeft.apply(this, arguments);
17376         this.sync();
17377     },
17378
17379     setTop : function(top){
17380         this.storeLeftTop(this.getLeft(true), top);
17381         supr.setTop.apply(this, arguments);
17382         this.sync();
17383     },
17384
17385     setLeftTop : function(left, top){
17386         this.storeLeftTop(left, top);
17387         supr.setLeftTop.apply(this, arguments);
17388         this.sync();
17389     },
17390
17391     setXY : function(xy, a, d, c, e){
17392         this.fixDisplay();
17393         this.beforeAction();
17394         this.storeXY(xy);
17395         var cb = this.createCB(c);
17396         supr.setXY.call(this, xy, a, d, cb, e);
17397         if(!a){
17398             cb();
17399         }
17400     },
17401
17402     // private
17403     createCB : function(c){
17404         var el = this;
17405         return function(){
17406             el.constrainXY();
17407             el.sync(true);
17408             if(c){
17409                 c();
17410             }
17411         };
17412     },
17413
17414     // overridden Element method
17415     setX : function(x, a, d, c, e){
17416         this.setXY([x, this.getY()], a, d, c, e);
17417     },
17418
17419     // overridden Element method
17420     setY : function(y, a, d, c, e){
17421         this.setXY([this.getX(), y], a, d, c, e);
17422     },
17423
17424     // overridden Element method
17425     setSize : function(w, h, a, d, c, e){
17426         this.beforeAction();
17427         var cb = this.createCB(c);
17428         supr.setSize.call(this, w, h, a, d, cb, e);
17429         if(!a){
17430             cb();
17431         }
17432     },
17433
17434     // overridden Element method
17435     setWidth : function(w, a, d, c, e){
17436         this.beforeAction();
17437         var cb = this.createCB(c);
17438         supr.setWidth.call(this, w, a, d, cb, e);
17439         if(!a){
17440             cb();
17441         }
17442     },
17443
17444     // overridden Element method
17445     setHeight : function(h, a, d, c, e){
17446         this.beforeAction();
17447         var cb = this.createCB(c);
17448         supr.setHeight.call(this, h, a, d, cb, e);
17449         if(!a){
17450             cb();
17451         }
17452     },
17453
17454     // overridden Element method
17455     setBounds : function(x, y, w, h, a, d, c, e){
17456         this.beforeAction();
17457         var cb = this.createCB(c);
17458         if(!a){
17459             this.storeXY([x, y]);
17460             supr.setXY.call(this, [x, y]);
17461             supr.setSize.call(this, w, h, a, d, cb, e);
17462             cb();
17463         }else{
17464             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17465         }
17466         return this;
17467     },
17468     
17469     /**
17470      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17471      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17472      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17473      * @param {Number} zindex The new z-index to set
17474      * @return {this} The Layer
17475      */
17476     setZIndex : function(zindex){
17477         this.zindex = zindex;
17478         this.setStyle("z-index", zindex + 2);
17479         if(this.shadow){
17480             this.shadow.setZIndex(zindex + 1);
17481         }
17482         if(this.shim){
17483             this.shim.setStyle("z-index", zindex);
17484         }
17485     }
17486 });
17487 })();/*
17488  * Original code for Roojs - LGPL
17489  * <script type="text/javascript">
17490  */
17491  
17492 /**
17493  * @class Roo.XComponent
17494  * A delayed Element creator...
17495  * Or a way to group chunks of interface together.
17496  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17497  *  used in conjunction with XComponent.build() it will create an instance of each element,
17498  *  then call addxtype() to build the User interface.
17499  * 
17500  * Mypart.xyx = new Roo.XComponent({
17501
17502     parent : 'Mypart.xyz', // empty == document.element.!!
17503     order : '001',
17504     name : 'xxxx'
17505     region : 'xxxx'
17506     disabled : function() {} 
17507      
17508     tree : function() { // return an tree of xtype declared components
17509         var MODULE = this;
17510         return 
17511         {
17512             xtype : 'NestedLayoutPanel',
17513             // technicall
17514         }
17515      ]
17516  *})
17517  *
17518  *
17519  * It can be used to build a big heiracy, with parent etc.
17520  * or you can just use this to render a single compoent to a dom element
17521  * MYPART.render(Roo.Element | String(id) | dom_element )
17522  *
17523  *
17524  * Usage patterns.
17525  *
17526  * Classic Roo
17527  *
17528  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17529  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17530  *
17531  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17532  *
17533  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17534  * - if mulitple topModules exist, the last one is defined as the top module.
17535  *
17536  * Embeded Roo
17537  * 
17538  * When the top level or multiple modules are to embedded into a existing HTML page,
17539  * the parent element can container '#id' of the element where the module will be drawn.
17540  *
17541  * Bootstrap Roo
17542  *
17543  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17544  * it relies more on a include mechanism, where sub modules are included into an outer page.
17545  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17546  * 
17547  * Bootstrap Roo Included elements
17548  *
17549  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17550  * hence confusing the component builder as it thinks there are multiple top level elements. 
17551  *
17552  * String Over-ride & Translations
17553  *
17554  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17555  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17556  * are needed. @see Roo.XComponent.overlayString  
17557  * 
17558  * 
17559  * 
17560  * @extends Roo.util.Observable
17561  * @constructor
17562  * @param cfg {Object} configuration of component
17563  * 
17564  */
17565 Roo.XComponent = function(cfg) {
17566     Roo.apply(this, cfg);
17567     this.addEvents({ 
17568         /**
17569              * @event built
17570              * Fires when this the componnt is built
17571              * @param {Roo.XComponent} c the component
17572              */
17573         'built' : true
17574         
17575     });
17576     this.region = this.region || 'center'; // default..
17577     Roo.XComponent.register(this);
17578     this.modules = false;
17579     this.el = false; // where the layout goes..
17580     
17581     
17582 }
17583 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17584     /**
17585      * @property el
17586      * The created element (with Roo.factory())
17587      * @type {Roo.Layout}
17588      */
17589     el  : false,
17590     
17591     /**
17592      * @property el
17593      * for BC  - use el in new code
17594      * @type {Roo.Layout}
17595      */
17596     panel : false,
17597     
17598     /**
17599      * @property layout
17600      * for BC  - use el in new code
17601      * @type {Roo.Layout}
17602      */
17603     layout : false,
17604     
17605      /**
17606      * @cfg {Function|boolean} disabled
17607      * If this module is disabled by some rule, return true from the funtion
17608      */
17609     disabled : false,
17610     
17611     /**
17612      * @cfg {String} parent 
17613      * Name of parent element which it get xtype added to..
17614      */
17615     parent: false,
17616     
17617     /**
17618      * @cfg {String} order
17619      * Used to set the order in which elements are created (usefull for multiple tabs)
17620      */
17621     
17622     order : false,
17623     /**
17624      * @cfg {String} name
17625      * String to display while loading.
17626      */
17627     name : false,
17628     /**
17629      * @cfg {String} region
17630      * Region to render component to (defaults to center)
17631      */
17632     region : 'center',
17633     
17634     /**
17635      * @cfg {Array} items
17636      * A single item array - the first element is the root of the tree..
17637      * It's done this way to stay compatible with the Xtype system...
17638      */
17639     items : false,
17640     
17641     /**
17642      * @property _tree
17643      * The method that retuns the tree of parts that make up this compoennt 
17644      * @type {function}
17645      */
17646     _tree  : false,
17647     
17648      /**
17649      * render
17650      * render element to dom or tree
17651      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17652      */
17653     
17654     render : function(el)
17655     {
17656         
17657         el = el || false;
17658         var hp = this.parent ? 1 : 0;
17659         Roo.debug &&  Roo.log(this);
17660         
17661         var tree = this._tree ? this._tree() : this.tree();
17662
17663         
17664         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17665             // if parent is a '#.....' string, then let's use that..
17666             var ename = this.parent.substr(1);
17667             this.parent = false;
17668             Roo.debug && Roo.log(ename);
17669             switch (ename) {
17670                 case 'bootstrap-body':
17671                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17672                         // this is the BorderLayout standard?
17673                        this.parent = { el : true };
17674                        break;
17675                     }
17676                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17677                         // need to insert stuff...
17678                         this.parent =  {
17679                              el : new Roo.bootstrap.layout.Border({
17680                                  el : document.body, 
17681                      
17682                                  center: {
17683                                     titlebar: false,
17684                                     autoScroll:false,
17685                                     closeOnTab: true,
17686                                     tabPosition: 'top',
17687                                       //resizeTabs: true,
17688                                     alwaysShowTabs: true,
17689                                     hideTabs: false
17690                                      //minTabWidth: 140
17691                                  }
17692                              })
17693                         
17694                          };
17695                          break;
17696                     }
17697                          
17698                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17699                         this.parent = { el :  new  Roo.bootstrap.Body() };
17700                         Roo.debug && Roo.log("setting el to doc body");
17701                          
17702                     } else {
17703                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17704                     }
17705                     break;
17706                 case 'bootstrap':
17707                     this.parent = { el : true};
17708                     // fall through
17709                 default:
17710                     el = Roo.get(ename);
17711                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17712                         this.parent = { el : true};
17713                     }
17714                     
17715                     break;
17716             }
17717                 
17718             
17719             if (!el && !this.parent) {
17720                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17721                 return;
17722             }
17723         }
17724         
17725         Roo.debug && Roo.log("EL:");
17726         Roo.debug && Roo.log(el);
17727         Roo.debug && Roo.log("this.parent.el:");
17728         Roo.debug && Roo.log(this.parent.el);
17729         
17730
17731         // altertive root elements ??? - we need a better way to indicate these.
17732         var is_alt = Roo.XComponent.is_alt ||
17733                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17734                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17735                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17736         
17737         
17738         
17739         if (!this.parent && is_alt) {
17740             //el = Roo.get(document.body);
17741             this.parent = { el : true };
17742         }
17743             
17744             
17745         
17746         if (!this.parent) {
17747             
17748             Roo.debug && Roo.log("no parent - creating one");
17749             
17750             el = el ? Roo.get(el) : false;      
17751             
17752             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17753                 
17754                 this.parent =  {
17755                     el : new Roo.bootstrap.layout.Border({
17756                         el: el || document.body,
17757                     
17758                         center: {
17759                             titlebar: false,
17760                             autoScroll:false,
17761                             closeOnTab: true,
17762                             tabPosition: 'top',
17763                              //resizeTabs: true,
17764                             alwaysShowTabs: false,
17765                             hideTabs: true,
17766                             minTabWidth: 140,
17767                             overflow: 'visible'
17768                          }
17769                      })
17770                 };
17771             } else {
17772             
17773                 // it's a top level one..
17774                 this.parent =  {
17775                     el : new Roo.BorderLayout(el || document.body, {
17776                         center: {
17777                             titlebar: false,
17778                             autoScroll:false,
17779                             closeOnTab: true,
17780                             tabPosition: 'top',
17781                              //resizeTabs: true,
17782                             alwaysShowTabs: el && hp? false :  true,
17783                             hideTabs: el || !hp ? true :  false,
17784                             minTabWidth: 140
17785                          }
17786                     })
17787                 };
17788             }
17789         }
17790         
17791         if (!this.parent.el) {
17792                 // probably an old style ctor, which has been disabled.
17793                 return;
17794
17795         }
17796                 // The 'tree' method is  '_tree now' 
17797             
17798         tree.region = tree.region || this.region;
17799         var is_body = false;
17800         if (this.parent.el === true) {
17801             // bootstrap... - body..
17802             if (el) {
17803                 tree.el = el;
17804             }
17805             this.parent.el = Roo.factory(tree);
17806             is_body = true;
17807         }
17808         
17809         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17810         this.fireEvent('built', this);
17811         
17812         this.panel = this.el;
17813         this.layout = this.panel.layout;
17814         this.parentLayout = this.parent.layout  || false;  
17815          
17816     }
17817     
17818 });
17819
17820 Roo.apply(Roo.XComponent, {
17821     /**
17822      * @property  hideProgress
17823      * true to disable the building progress bar.. usefull on single page renders.
17824      * @type Boolean
17825      */
17826     hideProgress : false,
17827     /**
17828      * @property  buildCompleted
17829      * True when the builder has completed building the interface.
17830      * @type Boolean
17831      */
17832     buildCompleted : false,
17833      
17834     /**
17835      * @property  topModule
17836      * the upper most module - uses document.element as it's constructor.
17837      * @type Object
17838      */
17839      
17840     topModule  : false,
17841       
17842     /**
17843      * @property  modules
17844      * array of modules to be created by registration system.
17845      * @type {Array} of Roo.XComponent
17846      */
17847     
17848     modules : [],
17849     /**
17850      * @property  elmodules
17851      * array of modules to be created by which use #ID 
17852      * @type {Array} of Roo.XComponent
17853      */
17854      
17855     elmodules : [],
17856
17857      /**
17858      * @property  is_alt
17859      * Is an alternative Root - normally used by bootstrap or other systems,
17860      *    where the top element in the tree can wrap 'body' 
17861      * @type {boolean}  (default false)
17862      */
17863      
17864     is_alt : false,
17865     /**
17866      * @property  build_from_html
17867      * Build elements from html - used by bootstrap HTML stuff 
17868      *    - this is cleared after build is completed
17869      * @type {boolean}    (default false)
17870      */
17871      
17872     build_from_html : false,
17873     /**
17874      * Register components to be built later.
17875      *
17876      * This solves the following issues
17877      * - Building is not done on page load, but after an authentication process has occured.
17878      * - Interface elements are registered on page load
17879      * - Parent Interface elements may not be loaded before child, so this handles that..
17880      * 
17881      *
17882      * example:
17883      * 
17884      * MyApp.register({
17885           order : '000001',
17886           module : 'Pman.Tab.projectMgr',
17887           region : 'center',
17888           parent : 'Pman.layout',
17889           disabled : false,  // or use a function..
17890         })
17891      
17892      * * @param {Object} details about module
17893      */
17894     register : function(obj) {
17895                 
17896         Roo.XComponent.event.fireEvent('register', obj);
17897         switch(typeof(obj.disabled) ) {
17898                 
17899             case 'undefined':
17900                 break;
17901             
17902             case 'function':
17903                 if ( obj.disabled() ) {
17904                         return;
17905                 }
17906                 break;
17907             
17908             default:
17909                 if (obj.disabled || obj.region == '#disabled') {
17910                         return;
17911                 }
17912                 break;
17913         }
17914                 
17915         this.modules.push(obj);
17916          
17917     },
17918     /**
17919      * convert a string to an object..
17920      * eg. 'AAA.BBB' -> finds AAA.BBB
17921
17922      */
17923     
17924     toObject : function(str)
17925     {
17926         if (!str || typeof(str) == 'object') {
17927             return str;
17928         }
17929         if (str.substring(0,1) == '#') {
17930             return str;
17931         }
17932
17933         var ar = str.split('.');
17934         var rt, o;
17935         rt = ar.shift();
17936             /** eval:var:o */
17937         try {
17938             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17939         } catch (e) {
17940             throw "Module not found : " + str;
17941         }
17942         
17943         if (o === false) {
17944             throw "Module not found : " + str;
17945         }
17946         Roo.each(ar, function(e) {
17947             if (typeof(o[e]) == 'undefined') {
17948                 throw "Module not found : " + str;
17949             }
17950             o = o[e];
17951         });
17952         
17953         return o;
17954         
17955     },
17956     
17957     
17958     /**
17959      * move modules into their correct place in the tree..
17960      * 
17961      */
17962     preBuild : function ()
17963     {
17964         var _t = this;
17965         Roo.each(this.modules , function (obj)
17966         {
17967             Roo.XComponent.event.fireEvent('beforebuild', obj);
17968             
17969             var opar = obj.parent;
17970             try { 
17971                 obj.parent = this.toObject(opar);
17972             } catch(e) {
17973                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17974                 return;
17975             }
17976             
17977             if (!obj.parent) {
17978                 Roo.debug && Roo.log("GOT top level module");
17979                 Roo.debug && Roo.log(obj);
17980                 obj.modules = new Roo.util.MixedCollection(false, 
17981                     function(o) { return o.order + '' }
17982                 );
17983                 this.topModule = obj;
17984                 return;
17985             }
17986                         // parent is a string (usually a dom element name..)
17987             if (typeof(obj.parent) == 'string') {
17988                 this.elmodules.push(obj);
17989                 return;
17990             }
17991             if (obj.parent.constructor != Roo.XComponent) {
17992                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17993             }
17994             if (!obj.parent.modules) {
17995                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17996                     function(o) { return o.order + '' }
17997                 );
17998             }
17999             if (obj.parent.disabled) {
18000                 obj.disabled = true;
18001             }
18002             obj.parent.modules.add(obj);
18003         }, this);
18004     },
18005     
18006      /**
18007      * make a list of modules to build.
18008      * @return {Array} list of modules. 
18009      */ 
18010     
18011     buildOrder : function()
18012     {
18013         var _this = this;
18014         var cmp = function(a,b) {   
18015             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18016         };
18017         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18018             throw "No top level modules to build";
18019         }
18020         
18021         // make a flat list in order of modules to build.
18022         var mods = this.topModule ? [ this.topModule ] : [];
18023                 
18024         
18025         // elmodules (is a list of DOM based modules )
18026         Roo.each(this.elmodules, function(e) {
18027             mods.push(e);
18028             if (!this.topModule &&
18029                 typeof(e.parent) == 'string' &&
18030                 e.parent.substring(0,1) == '#' &&
18031                 Roo.get(e.parent.substr(1))
18032                ) {
18033                 
18034                 _this.topModule = e;
18035             }
18036             
18037         });
18038
18039         
18040         // add modules to their parents..
18041         var addMod = function(m) {
18042             Roo.debug && Roo.log("build Order: add: " + m.name);
18043                 
18044             mods.push(m);
18045             if (m.modules && !m.disabled) {
18046                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18047                 m.modules.keySort('ASC',  cmp );
18048                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18049     
18050                 m.modules.each(addMod);
18051             } else {
18052                 Roo.debug && Roo.log("build Order: no child modules");
18053             }
18054             // not sure if this is used any more..
18055             if (m.finalize) {
18056                 m.finalize.name = m.name + " (clean up) ";
18057                 mods.push(m.finalize);
18058             }
18059             
18060         }
18061         if (this.topModule && this.topModule.modules) { 
18062             this.topModule.modules.keySort('ASC',  cmp );
18063             this.topModule.modules.each(addMod);
18064         } 
18065         return mods;
18066     },
18067     
18068      /**
18069      * Build the registered modules.
18070      * @param {Object} parent element.
18071      * @param {Function} optional method to call after module has been added.
18072      * 
18073      */ 
18074    
18075     build : function(opts) 
18076     {
18077         
18078         if (typeof(opts) != 'undefined') {
18079             Roo.apply(this,opts);
18080         }
18081         
18082         this.preBuild();
18083         var mods = this.buildOrder();
18084       
18085         //this.allmods = mods;
18086         //Roo.debug && Roo.log(mods);
18087         //return;
18088         if (!mods.length) { // should not happen
18089             throw "NO modules!!!";
18090         }
18091         
18092         
18093         var msg = "Building Interface...";
18094         // flash it up as modal - so we store the mask!?
18095         if (!this.hideProgress && Roo.MessageBox) {
18096             Roo.MessageBox.show({ title: 'loading' });
18097             Roo.MessageBox.show({
18098                title: "Please wait...",
18099                msg: msg,
18100                width:450,
18101                progress:true,
18102                buttons : false,
18103                closable:false,
18104                modal: false
18105               
18106             });
18107         }
18108         var total = mods.length;
18109         
18110         var _this = this;
18111         var progressRun = function() {
18112             if (!mods.length) {
18113                 Roo.debug && Roo.log('hide?');
18114                 if (!this.hideProgress && Roo.MessageBox) {
18115                     Roo.MessageBox.hide();
18116                 }
18117                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18118                 
18119                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18120                 
18121                 // THE END...
18122                 return false;   
18123             }
18124             
18125             var m = mods.shift();
18126             
18127             
18128             Roo.debug && Roo.log(m);
18129             // not sure if this is supported any more.. - modules that are are just function
18130             if (typeof(m) == 'function') { 
18131                 m.call(this);
18132                 return progressRun.defer(10, _this);
18133             } 
18134             
18135             
18136             msg = "Building Interface " + (total  - mods.length) + 
18137                     " of " + total + 
18138                     (m.name ? (' - ' + m.name) : '');
18139                         Roo.debug && Roo.log(msg);
18140             if (!_this.hideProgress &&  Roo.MessageBox) { 
18141                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18142             }
18143             
18144          
18145             // is the module disabled?
18146             var disabled = (typeof(m.disabled) == 'function') ?
18147                 m.disabled.call(m.module.disabled) : m.disabled;    
18148             
18149             
18150             if (disabled) {
18151                 return progressRun(); // we do not update the display!
18152             }
18153             
18154             // now build 
18155             
18156                         
18157                         
18158             m.render();
18159             // it's 10 on top level, and 1 on others??? why...
18160             return progressRun.defer(10, _this);
18161              
18162         }
18163         progressRun.defer(1, _this);
18164      
18165         
18166         
18167     },
18168     /**
18169      * Overlay a set of modified strings onto a component
18170      * This is dependant on our builder exporting the strings and 'named strings' elements.
18171      * 
18172      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18173      * @param {Object} associative array of 'named' string and it's new value.
18174      * 
18175      */
18176         overlayStrings : function( component, strings )
18177     {
18178         if (typeof(component['_named_strings']) == 'undefined') {
18179             throw "ERROR: component does not have _named_strings";
18180         }
18181         for ( var k in strings ) {
18182             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18183             if (md !== false) {
18184                 component['_strings'][md] = strings[k];
18185             } else {
18186                 Roo.log('could not find named string: ' + k + ' in');
18187                 Roo.log(component);
18188             }
18189             
18190         }
18191         
18192     },
18193     
18194         
18195         /**
18196          * Event Object.
18197          *
18198          *
18199          */
18200         event: false, 
18201     /**
18202          * wrapper for event.on - aliased later..  
18203          * Typically use to register a event handler for register:
18204          *
18205          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18206          *
18207          */
18208     on : false
18209    
18210     
18211     
18212 });
18213
18214 Roo.XComponent.event = new Roo.util.Observable({
18215                 events : { 
18216                         /**
18217                          * @event register
18218                          * Fires when an Component is registered,
18219                          * set the disable property on the Component to stop registration.
18220                          * @param {Roo.XComponent} c the component being registerd.
18221                          * 
18222                          */
18223                         'register' : true,
18224             /**
18225                          * @event beforebuild
18226                          * Fires before each Component is built
18227                          * can be used to apply permissions.
18228                          * @param {Roo.XComponent} c the component being registerd.
18229                          * 
18230                          */
18231                         'beforebuild' : true,
18232                         /**
18233                          * @event buildcomplete
18234                          * Fires on the top level element when all elements have been built
18235                          * @param {Roo.XComponent} the top level component.
18236                          */
18237                         'buildcomplete' : true
18238                         
18239                 }
18240 });
18241
18242 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18243  //
18244  /**
18245  * marked - a markdown parser
18246  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18247  * https://github.com/chjj/marked
18248  */
18249
18250
18251 /**
18252  *
18253  * Roo.Markdown - is a very crude wrapper around marked..
18254  *
18255  * usage:
18256  * 
18257  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18258  * 
18259  * Note: move the sample code to the bottom of this
18260  * file before uncommenting it.
18261  *
18262  */
18263
18264 Roo.Markdown = {};
18265 Roo.Markdown.toHtml = function(text) {
18266     
18267     var c = new Roo.Markdown.marked.setOptions({
18268             renderer: new Roo.Markdown.marked.Renderer(),
18269             gfm: true,
18270             tables: true,
18271             breaks: false,
18272             pedantic: false,
18273             sanitize: false,
18274             smartLists: true,
18275             smartypants: false
18276           });
18277     // A FEW HACKS!!?
18278     
18279     text = text.replace(/\\\n/g,' ');
18280     return Roo.Markdown.marked(text);
18281 };
18282 //
18283 // converter
18284 //
18285 // Wraps all "globals" so that the only thing
18286 // exposed is makeHtml().
18287 //
18288 (function() {
18289     
18290      /**
18291          * eval:var:escape
18292          * eval:var:unescape
18293          * eval:var:replace
18294          */
18295       
18296     /**
18297      * Helpers
18298      */
18299     
18300     var escape = function (html, encode) {
18301       return html
18302         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18303         .replace(/</g, '&lt;')
18304         .replace(/>/g, '&gt;')
18305         .replace(/"/g, '&quot;')
18306         .replace(/'/g, '&#39;');
18307     }
18308     
18309     var unescape = function (html) {
18310         // explicitly match decimal, hex, and named HTML entities 
18311       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18312         n = n.toLowerCase();
18313         if (n === 'colon') { return ':'; }
18314         if (n.charAt(0) === '#') {
18315           return n.charAt(1) === 'x'
18316             ? String.fromCharCode(parseInt(n.substring(2), 16))
18317             : String.fromCharCode(+n.substring(1));
18318         }
18319         return '';
18320       });
18321     }
18322     
18323     var replace = function (regex, opt) {
18324       regex = regex.source;
18325       opt = opt || '';
18326       return function self(name, val) {
18327         if (!name) { return new RegExp(regex, opt); }
18328         val = val.source || val;
18329         val = val.replace(/(^|[^\[])\^/g, '$1');
18330         regex = regex.replace(name, val);
18331         return self;
18332       };
18333     }
18334
18335
18336          /**
18337          * eval:var:noop
18338     */
18339     var noop = function () {}
18340     noop.exec = noop;
18341     
18342          /**
18343          * eval:var:merge
18344     */
18345     var merge = function (obj) {
18346       var i = 1
18347         , target
18348         , key;
18349     
18350       for (; i < arguments.length; i++) {
18351         target = arguments[i];
18352         for (key in target) {
18353           if (Object.prototype.hasOwnProperty.call(target, key)) {
18354             obj[key] = target[key];
18355           }
18356         }
18357       }
18358     
18359       return obj;
18360     }
18361     
18362     
18363     /**
18364      * Block-Level Grammar
18365      */
18366     
18367     
18368     
18369     
18370     var block = {
18371       newline: /^\n+/,
18372       code: /^( {4}[^\n]+\n*)+/,
18373       fences: noop,
18374       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18375       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18376       nptable: noop,
18377       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18378       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18379       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18380       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18381       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18382       table: noop,
18383       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18384       text: /^[^\n]+/
18385     };
18386     
18387     block.bullet = /(?:[*+-]|\d+\.)/;
18388     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18389     block.item = replace(block.item, 'gm')
18390       (/bull/g, block.bullet)
18391       ();
18392     
18393     block.list = replace(block.list)
18394       (/bull/g, block.bullet)
18395       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18396       ('def', '\\n+(?=' + block.def.source + ')')
18397       ();
18398     
18399     block.blockquote = replace(block.blockquote)
18400       ('def', block.def)
18401       ();
18402     
18403     block._tag = '(?!(?:'
18404       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18405       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18406       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18407     
18408     block.html = replace(block.html)
18409       ('comment', /<!--[\s\S]*?-->/)
18410       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18411       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18412       (/tag/g, block._tag)
18413       ();
18414     
18415     block.paragraph = replace(block.paragraph)
18416       ('hr', block.hr)
18417       ('heading', block.heading)
18418       ('lheading', block.lheading)
18419       ('blockquote', block.blockquote)
18420       ('tag', '<' + block._tag)
18421       ('def', block.def)
18422       ();
18423     
18424     /**
18425      * Normal Block Grammar
18426      */
18427     
18428     block.normal = merge({}, block);
18429     
18430     /**
18431      * GFM Block Grammar
18432      */
18433     
18434     block.gfm = merge({}, block.normal, {
18435       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18436       paragraph: /^/,
18437       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18438     });
18439     
18440     block.gfm.paragraph = replace(block.paragraph)
18441       ('(?!', '(?!'
18442         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18443         + block.list.source.replace('\\1', '\\3') + '|')
18444       ();
18445     
18446     /**
18447      * GFM + Tables Block Grammar
18448      */
18449     
18450     block.tables = merge({}, block.gfm, {
18451       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18452       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18453     });
18454     
18455     /**
18456      * Block Lexer
18457      */
18458     
18459     var Lexer = function (options) {
18460       this.tokens = [];
18461       this.tokens.links = {};
18462       this.options = options || marked.defaults;
18463       this.rules = block.normal;
18464     
18465       if (this.options.gfm) {
18466         if (this.options.tables) {
18467           this.rules = block.tables;
18468         } else {
18469           this.rules = block.gfm;
18470         }
18471       }
18472     }
18473     
18474     /**
18475      * Expose Block Rules
18476      */
18477     
18478     Lexer.rules = block;
18479     
18480     /**
18481      * Static Lex Method
18482      */
18483     
18484     Lexer.lex = function(src, options) {
18485       var lexer = new Lexer(options);
18486       return lexer.lex(src);
18487     };
18488     
18489     /**
18490      * Preprocessing
18491      */
18492     
18493     Lexer.prototype.lex = function(src) {
18494       src = src
18495         .replace(/\r\n|\r/g, '\n')
18496         .replace(/\t/g, '    ')
18497         .replace(/\u00a0/g, ' ')
18498         .replace(/\u2424/g, '\n');
18499     
18500       return this.token(src, true);
18501     };
18502     
18503     /**
18504      * Lexing
18505      */
18506     
18507     Lexer.prototype.token = function(src, top, bq) {
18508       var src = src.replace(/^ +$/gm, '')
18509         , next
18510         , loose
18511         , cap
18512         , bull
18513         , b
18514         , item
18515         , space
18516         , i
18517         , l;
18518     
18519       while (src) {
18520         // newline
18521         if (cap = this.rules.newline.exec(src)) {
18522           src = src.substring(cap[0].length);
18523           if (cap[0].length > 1) {
18524             this.tokens.push({
18525               type: 'space'
18526             });
18527           }
18528         }
18529     
18530         // code
18531         if (cap = this.rules.code.exec(src)) {
18532           src = src.substring(cap[0].length);
18533           cap = cap[0].replace(/^ {4}/gm, '');
18534           this.tokens.push({
18535             type: 'code',
18536             text: !this.options.pedantic
18537               ? cap.replace(/\n+$/, '')
18538               : cap
18539           });
18540           continue;
18541         }
18542     
18543         // fences (gfm)
18544         if (cap = this.rules.fences.exec(src)) {
18545           src = src.substring(cap[0].length);
18546           this.tokens.push({
18547             type: 'code',
18548             lang: cap[2],
18549             text: cap[3] || ''
18550           });
18551           continue;
18552         }
18553     
18554         // heading
18555         if (cap = this.rules.heading.exec(src)) {
18556           src = src.substring(cap[0].length);
18557           this.tokens.push({
18558             type: 'heading',
18559             depth: cap[1].length,
18560             text: cap[2]
18561           });
18562           continue;
18563         }
18564     
18565         // table no leading pipe (gfm)
18566         if (top && (cap = this.rules.nptable.exec(src))) {
18567           src = src.substring(cap[0].length);
18568     
18569           item = {
18570             type: 'table',
18571             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18572             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18573             cells: cap[3].replace(/\n$/, '').split('\n')
18574           };
18575     
18576           for (i = 0; i < item.align.length; i++) {
18577             if (/^ *-+: *$/.test(item.align[i])) {
18578               item.align[i] = 'right';
18579             } else if (/^ *:-+: *$/.test(item.align[i])) {
18580               item.align[i] = 'center';
18581             } else if (/^ *:-+ *$/.test(item.align[i])) {
18582               item.align[i] = 'left';
18583             } else {
18584               item.align[i] = null;
18585             }
18586           }
18587     
18588           for (i = 0; i < item.cells.length; i++) {
18589             item.cells[i] = item.cells[i].split(/ *\| */);
18590           }
18591     
18592           this.tokens.push(item);
18593     
18594           continue;
18595         }
18596     
18597         // lheading
18598         if (cap = this.rules.lheading.exec(src)) {
18599           src = src.substring(cap[0].length);
18600           this.tokens.push({
18601             type: 'heading',
18602             depth: cap[2] === '=' ? 1 : 2,
18603             text: cap[1]
18604           });
18605           continue;
18606         }
18607     
18608         // hr
18609         if (cap = this.rules.hr.exec(src)) {
18610           src = src.substring(cap[0].length);
18611           this.tokens.push({
18612             type: 'hr'
18613           });
18614           continue;
18615         }
18616     
18617         // blockquote
18618         if (cap = this.rules.blockquote.exec(src)) {
18619           src = src.substring(cap[0].length);
18620     
18621           this.tokens.push({
18622             type: 'blockquote_start'
18623           });
18624     
18625           cap = cap[0].replace(/^ *> ?/gm, '');
18626     
18627           // Pass `top` to keep the current
18628           // "toplevel" state. This is exactly
18629           // how markdown.pl works.
18630           this.token(cap, top, true);
18631     
18632           this.tokens.push({
18633             type: 'blockquote_end'
18634           });
18635     
18636           continue;
18637         }
18638     
18639         // list
18640         if (cap = this.rules.list.exec(src)) {
18641           src = src.substring(cap[0].length);
18642           bull = cap[2];
18643     
18644           this.tokens.push({
18645             type: 'list_start',
18646             ordered: bull.length > 1
18647           });
18648     
18649           // Get each top-level item.
18650           cap = cap[0].match(this.rules.item);
18651     
18652           next = false;
18653           l = cap.length;
18654           i = 0;
18655     
18656           for (; i < l; i++) {
18657             item = cap[i];
18658     
18659             // Remove the list item's bullet
18660             // so it is seen as the next token.
18661             space = item.length;
18662             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18663     
18664             // Outdent whatever the
18665             // list item contains. Hacky.
18666             if (~item.indexOf('\n ')) {
18667               space -= item.length;
18668               item = !this.options.pedantic
18669                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18670                 : item.replace(/^ {1,4}/gm, '');
18671             }
18672     
18673             // Determine whether the next list item belongs here.
18674             // Backpedal if it does not belong in this list.
18675             if (this.options.smartLists && i !== l - 1) {
18676               b = block.bullet.exec(cap[i + 1])[0];
18677               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18678                 src = cap.slice(i + 1).join('\n') + src;
18679                 i = l - 1;
18680               }
18681             }
18682     
18683             // Determine whether item is loose or not.
18684             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18685             // for discount behavior.
18686             loose = next || /\n\n(?!\s*$)/.test(item);
18687             if (i !== l - 1) {
18688               next = item.charAt(item.length - 1) === '\n';
18689               if (!loose) { loose = next; }
18690             }
18691     
18692             this.tokens.push({
18693               type: loose
18694                 ? 'loose_item_start'
18695                 : 'list_item_start'
18696             });
18697     
18698             // Recurse.
18699             this.token(item, false, bq);
18700     
18701             this.tokens.push({
18702               type: 'list_item_end'
18703             });
18704           }
18705     
18706           this.tokens.push({
18707             type: 'list_end'
18708           });
18709     
18710           continue;
18711         }
18712     
18713         // html
18714         if (cap = this.rules.html.exec(src)) {
18715           src = src.substring(cap[0].length);
18716           this.tokens.push({
18717             type: this.options.sanitize
18718               ? 'paragraph'
18719               : 'html',
18720             pre: !this.options.sanitizer
18721               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18722             text: cap[0]
18723           });
18724           continue;
18725         }
18726     
18727         // def
18728         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18729           src = src.substring(cap[0].length);
18730           this.tokens.links[cap[1].toLowerCase()] = {
18731             href: cap[2],
18732             title: cap[3]
18733           };
18734           continue;
18735         }
18736     
18737         // table (gfm)
18738         if (top && (cap = this.rules.table.exec(src))) {
18739           src = src.substring(cap[0].length);
18740     
18741           item = {
18742             type: 'table',
18743             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18744             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18745             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18746           };
18747     
18748           for (i = 0; i < item.align.length; i++) {
18749             if (/^ *-+: *$/.test(item.align[i])) {
18750               item.align[i] = 'right';
18751             } else if (/^ *:-+: *$/.test(item.align[i])) {
18752               item.align[i] = 'center';
18753             } else if (/^ *:-+ *$/.test(item.align[i])) {
18754               item.align[i] = 'left';
18755             } else {
18756               item.align[i] = null;
18757             }
18758           }
18759     
18760           for (i = 0; i < item.cells.length; i++) {
18761             item.cells[i] = item.cells[i]
18762               .replace(/^ *\| *| *\| *$/g, '')
18763               .split(/ *\| */);
18764           }
18765     
18766           this.tokens.push(item);
18767     
18768           continue;
18769         }
18770     
18771         // top-level paragraph
18772         if (top && (cap = this.rules.paragraph.exec(src))) {
18773           src = src.substring(cap[0].length);
18774           this.tokens.push({
18775             type: 'paragraph',
18776             text: cap[1].charAt(cap[1].length - 1) === '\n'
18777               ? cap[1].slice(0, -1)
18778               : cap[1]
18779           });
18780           continue;
18781         }
18782     
18783         // text
18784         if (cap = this.rules.text.exec(src)) {
18785           // Top-level should never reach here.
18786           src = src.substring(cap[0].length);
18787           this.tokens.push({
18788             type: 'text',
18789             text: cap[0]
18790           });
18791           continue;
18792         }
18793     
18794         if (src) {
18795           throw new
18796             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18797         }
18798       }
18799     
18800       return this.tokens;
18801     };
18802     
18803     /**
18804      * Inline-Level Grammar
18805      */
18806     
18807     var inline = {
18808       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18809       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18810       url: noop,
18811       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18812       link: /^!?\[(inside)\]\(href\)/,
18813       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18814       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18815       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18816       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18817       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18818       br: /^ {2,}\n(?!\s*$)/,
18819       del: noop,
18820       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18821     };
18822     
18823     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18824     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18825     
18826     inline.link = replace(inline.link)
18827       ('inside', inline._inside)
18828       ('href', inline._href)
18829       ();
18830     
18831     inline.reflink = replace(inline.reflink)
18832       ('inside', inline._inside)
18833       ();
18834     
18835     /**
18836      * Normal Inline Grammar
18837      */
18838     
18839     inline.normal = merge({}, inline);
18840     
18841     /**
18842      * Pedantic Inline Grammar
18843      */
18844     
18845     inline.pedantic = merge({}, inline.normal, {
18846       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18847       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18848     });
18849     
18850     /**
18851      * GFM Inline Grammar
18852      */
18853     
18854     inline.gfm = merge({}, inline.normal, {
18855       escape: replace(inline.escape)('])', '~|])')(),
18856       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18857       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18858       text: replace(inline.text)
18859         (']|', '~]|')
18860         ('|', '|https?://|')
18861         ()
18862     });
18863     
18864     /**
18865      * GFM + Line Breaks Inline Grammar
18866      */
18867     
18868     inline.breaks = merge({}, inline.gfm, {
18869       br: replace(inline.br)('{2,}', '*')(),
18870       text: replace(inline.gfm.text)('{2,}', '*')()
18871     });
18872     
18873     /**
18874      * Inline Lexer & Compiler
18875      */
18876     
18877     var InlineLexer  = function (links, options) {
18878       this.options = options || marked.defaults;
18879       this.links = links;
18880       this.rules = inline.normal;
18881       this.renderer = this.options.renderer || new Renderer;
18882       this.renderer.options = this.options;
18883     
18884       if (!this.links) {
18885         throw new
18886           Error('Tokens array requires a `links` property.');
18887       }
18888     
18889       if (this.options.gfm) {
18890         if (this.options.breaks) {
18891           this.rules = inline.breaks;
18892         } else {
18893           this.rules = inline.gfm;
18894         }
18895       } else if (this.options.pedantic) {
18896         this.rules = inline.pedantic;
18897       }
18898     }
18899     
18900     /**
18901      * Expose Inline Rules
18902      */
18903     
18904     InlineLexer.rules = inline;
18905     
18906     /**
18907      * Static Lexing/Compiling Method
18908      */
18909     
18910     InlineLexer.output = function(src, links, options) {
18911       var inline = new InlineLexer(links, options);
18912       return inline.output(src);
18913     };
18914     
18915     /**
18916      * Lexing/Compiling
18917      */
18918     
18919     InlineLexer.prototype.output = function(src) {
18920       var out = ''
18921         , link
18922         , text
18923         , href
18924         , cap;
18925     
18926       while (src) {
18927         // escape
18928         if (cap = this.rules.escape.exec(src)) {
18929           src = src.substring(cap[0].length);
18930           out += cap[1];
18931           continue;
18932         }
18933     
18934         // autolink
18935         if (cap = this.rules.autolink.exec(src)) {
18936           src = src.substring(cap[0].length);
18937           if (cap[2] === '@') {
18938             text = cap[1].charAt(6) === ':'
18939               ? this.mangle(cap[1].substring(7))
18940               : this.mangle(cap[1]);
18941             href = this.mangle('mailto:') + text;
18942           } else {
18943             text = escape(cap[1]);
18944             href = text;
18945           }
18946           out += this.renderer.link(href, null, text);
18947           continue;
18948         }
18949     
18950         // url (gfm)
18951         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18952           src = src.substring(cap[0].length);
18953           text = escape(cap[1]);
18954           href = text;
18955           out += this.renderer.link(href, null, text);
18956           continue;
18957         }
18958     
18959         // tag
18960         if (cap = this.rules.tag.exec(src)) {
18961           if (!this.inLink && /^<a /i.test(cap[0])) {
18962             this.inLink = true;
18963           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18964             this.inLink = false;
18965           }
18966           src = src.substring(cap[0].length);
18967           out += this.options.sanitize
18968             ? this.options.sanitizer
18969               ? this.options.sanitizer(cap[0])
18970               : escape(cap[0])
18971             : cap[0];
18972           continue;
18973         }
18974     
18975         // link
18976         if (cap = this.rules.link.exec(src)) {
18977           src = src.substring(cap[0].length);
18978           this.inLink = true;
18979           out += this.outputLink(cap, {
18980             href: cap[2],
18981             title: cap[3]
18982           });
18983           this.inLink = false;
18984           continue;
18985         }
18986     
18987         // reflink, nolink
18988         if ((cap = this.rules.reflink.exec(src))
18989             || (cap = this.rules.nolink.exec(src))) {
18990           src = src.substring(cap[0].length);
18991           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18992           link = this.links[link.toLowerCase()];
18993           if (!link || !link.href) {
18994             out += cap[0].charAt(0);
18995             src = cap[0].substring(1) + src;
18996             continue;
18997           }
18998           this.inLink = true;
18999           out += this.outputLink(cap, link);
19000           this.inLink = false;
19001           continue;
19002         }
19003     
19004         // strong
19005         if (cap = this.rules.strong.exec(src)) {
19006           src = src.substring(cap[0].length);
19007           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19008           continue;
19009         }
19010     
19011         // em
19012         if (cap = this.rules.em.exec(src)) {
19013           src = src.substring(cap[0].length);
19014           out += this.renderer.em(this.output(cap[2] || cap[1]));
19015           continue;
19016         }
19017     
19018         // code
19019         if (cap = this.rules.code.exec(src)) {
19020           src = src.substring(cap[0].length);
19021           out += this.renderer.codespan(escape(cap[2], true));
19022           continue;
19023         }
19024     
19025         // br
19026         if (cap = this.rules.br.exec(src)) {
19027           src = src.substring(cap[0].length);
19028           out += this.renderer.br();
19029           continue;
19030         }
19031     
19032         // del (gfm)
19033         if (cap = this.rules.del.exec(src)) {
19034           src = src.substring(cap[0].length);
19035           out += this.renderer.del(this.output(cap[1]));
19036           continue;
19037         }
19038     
19039         // text
19040         if (cap = this.rules.text.exec(src)) {
19041           src = src.substring(cap[0].length);
19042           out += this.renderer.text(escape(this.smartypants(cap[0])));
19043           continue;
19044         }
19045     
19046         if (src) {
19047           throw new
19048             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19049         }
19050       }
19051     
19052       return out;
19053     };
19054     
19055     /**
19056      * Compile Link
19057      */
19058     
19059     InlineLexer.prototype.outputLink = function(cap, link) {
19060       var href = escape(link.href)
19061         , title = link.title ? escape(link.title) : null;
19062     
19063       return cap[0].charAt(0) !== '!'
19064         ? this.renderer.link(href, title, this.output(cap[1]))
19065         : this.renderer.image(href, title, escape(cap[1]));
19066     };
19067     
19068     /**
19069      * Smartypants Transformations
19070      */
19071     
19072     InlineLexer.prototype.smartypants = function(text) {
19073       if (!this.options.smartypants)  { return text; }
19074       return text
19075         // em-dashes
19076         .replace(/---/g, '\u2014')
19077         // en-dashes
19078         .replace(/--/g, '\u2013')
19079         // opening singles
19080         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19081         // closing singles & apostrophes
19082         .replace(/'/g, '\u2019')
19083         // opening doubles
19084         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19085         // closing doubles
19086         .replace(/"/g, '\u201d')
19087         // ellipses
19088         .replace(/\.{3}/g, '\u2026');
19089     };
19090     
19091     /**
19092      * Mangle Links
19093      */
19094     
19095     InlineLexer.prototype.mangle = function(text) {
19096       if (!this.options.mangle) { return text; }
19097       var out = ''
19098         , l = text.length
19099         , i = 0
19100         , ch;
19101     
19102       for (; i < l; i++) {
19103         ch = text.charCodeAt(i);
19104         if (Math.random() > 0.5) {
19105           ch = 'x' + ch.toString(16);
19106         }
19107         out += '&#' + ch + ';';
19108       }
19109     
19110       return out;
19111     };
19112     
19113     /**
19114      * Renderer
19115      */
19116     
19117      /**
19118          * eval:var:Renderer
19119     */
19120     
19121     var Renderer   = function (options) {
19122       this.options = options || {};
19123     }
19124     
19125     Renderer.prototype.code = function(code, lang, escaped) {
19126       if (this.options.highlight) {
19127         var out = this.options.highlight(code, lang);
19128         if (out != null && out !== code) {
19129           escaped = true;
19130           code = out;
19131         }
19132       } else {
19133             // hack!!! - it's already escapeD?
19134             escaped = true;
19135       }
19136     
19137       if (!lang) {
19138         return '<pre><code>'
19139           + (escaped ? code : escape(code, true))
19140           + '\n</code></pre>';
19141       }
19142     
19143       return '<pre><code class="'
19144         + this.options.langPrefix
19145         + escape(lang, true)
19146         + '">'
19147         + (escaped ? code : escape(code, true))
19148         + '\n</code></pre>\n';
19149     };
19150     
19151     Renderer.prototype.blockquote = function(quote) {
19152       return '<blockquote>\n' + quote + '</blockquote>\n';
19153     };
19154     
19155     Renderer.prototype.html = function(html) {
19156       return html;
19157     };
19158     
19159     Renderer.prototype.heading = function(text, level, raw) {
19160       return '<h'
19161         + level
19162         + ' id="'
19163         + this.options.headerPrefix
19164         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19165         + '">'
19166         + text
19167         + '</h'
19168         + level
19169         + '>\n';
19170     };
19171     
19172     Renderer.prototype.hr = function() {
19173       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19174     };
19175     
19176     Renderer.prototype.list = function(body, ordered) {
19177       var type = ordered ? 'ol' : 'ul';
19178       return '<' + type + '>\n' + body + '</' + type + '>\n';
19179     };
19180     
19181     Renderer.prototype.listitem = function(text) {
19182       return '<li>' + text + '</li>\n';
19183     };
19184     
19185     Renderer.prototype.paragraph = function(text) {
19186       return '<p>' + text + '</p>\n';
19187     };
19188     
19189     Renderer.prototype.table = function(header, body) {
19190       return '<table class="table table-striped">\n'
19191         + '<thead>\n'
19192         + header
19193         + '</thead>\n'
19194         + '<tbody>\n'
19195         + body
19196         + '</tbody>\n'
19197         + '</table>\n';
19198     };
19199     
19200     Renderer.prototype.tablerow = function(content) {
19201       return '<tr>\n' + content + '</tr>\n';
19202     };
19203     
19204     Renderer.prototype.tablecell = function(content, flags) {
19205       var type = flags.header ? 'th' : 'td';
19206       var tag = flags.align
19207         ? '<' + type + ' style="text-align:' + flags.align + '">'
19208         : '<' + type + '>';
19209       return tag + content + '</' + type + '>\n';
19210     };
19211     
19212     // span level renderer
19213     Renderer.prototype.strong = function(text) {
19214       return '<strong>' + text + '</strong>';
19215     };
19216     
19217     Renderer.prototype.em = function(text) {
19218       return '<em>' + text + '</em>';
19219     };
19220     
19221     Renderer.prototype.codespan = function(text) {
19222       return '<code>' + text + '</code>';
19223     };
19224     
19225     Renderer.prototype.br = function() {
19226       return this.options.xhtml ? '<br/>' : '<br>';
19227     };
19228     
19229     Renderer.prototype.del = function(text) {
19230       return '<del>' + text + '</del>';
19231     };
19232     
19233     Renderer.prototype.link = function(href, title, text) {
19234       if (this.options.sanitize) {
19235         try {
19236           var prot = decodeURIComponent(unescape(href))
19237             .replace(/[^\w:]/g, '')
19238             .toLowerCase();
19239         } catch (e) {
19240           return '';
19241         }
19242         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19243           return '';
19244         }
19245       }
19246       var out = '<a href="' + href + '"';
19247       if (title) {
19248         out += ' title="' + title + '"';
19249       }
19250       out += '>' + text + '</a>';
19251       return out;
19252     };
19253     
19254     Renderer.prototype.image = function(href, title, text) {
19255       var out = '<img src="' + href + '" alt="' + text + '"';
19256       if (title) {
19257         out += ' title="' + title + '"';
19258       }
19259       out += this.options.xhtml ? '/>' : '>';
19260       return out;
19261     };
19262     
19263     Renderer.prototype.text = function(text) {
19264       return text;
19265     };
19266     
19267     /**
19268      * Parsing & Compiling
19269      */
19270          /**
19271          * eval:var:Parser
19272     */
19273     
19274     var Parser= function (options) {
19275       this.tokens = [];
19276       this.token = null;
19277       this.options = options || marked.defaults;
19278       this.options.renderer = this.options.renderer || new Renderer;
19279       this.renderer = this.options.renderer;
19280       this.renderer.options = this.options;
19281     }
19282     
19283     /**
19284      * Static Parse Method
19285      */
19286     
19287     Parser.parse = function(src, options, renderer) {
19288       var parser = new Parser(options, renderer);
19289       return parser.parse(src);
19290     };
19291     
19292     /**
19293      * Parse Loop
19294      */
19295     
19296     Parser.prototype.parse = function(src) {
19297       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19298       this.tokens = src.reverse();
19299     
19300       var out = '';
19301       while (this.next()) {
19302         out += this.tok();
19303       }
19304     
19305       return out;
19306     };
19307     
19308     /**
19309      * Next Token
19310      */
19311     
19312     Parser.prototype.next = function() {
19313       return this.token = this.tokens.pop();
19314     };
19315     
19316     /**
19317      * Preview Next Token
19318      */
19319     
19320     Parser.prototype.peek = function() {
19321       return this.tokens[this.tokens.length - 1] || 0;
19322     };
19323     
19324     /**
19325      * Parse Text Tokens
19326      */
19327     
19328     Parser.prototype.parseText = function() {
19329       var body = this.token.text;
19330     
19331       while (this.peek().type === 'text') {
19332         body += '\n' + this.next().text;
19333       }
19334     
19335       return this.inline.output(body);
19336     };
19337     
19338     /**
19339      * Parse Current Token
19340      */
19341     
19342     Parser.prototype.tok = function() {
19343       switch (this.token.type) {
19344         case 'space': {
19345           return '';
19346         }
19347         case 'hr': {
19348           return this.renderer.hr();
19349         }
19350         case 'heading': {
19351           return this.renderer.heading(
19352             this.inline.output(this.token.text),
19353             this.token.depth,
19354             this.token.text);
19355         }
19356         case 'code': {
19357           return this.renderer.code(this.token.text,
19358             this.token.lang,
19359             this.token.escaped);
19360         }
19361         case 'table': {
19362           var header = ''
19363             , body = ''
19364             , i
19365             , row
19366             , cell
19367             , flags
19368             , j;
19369     
19370           // header
19371           cell = '';
19372           for (i = 0; i < this.token.header.length; i++) {
19373             flags = { header: true, align: this.token.align[i] };
19374             cell += this.renderer.tablecell(
19375               this.inline.output(this.token.header[i]),
19376               { header: true, align: this.token.align[i] }
19377             );
19378           }
19379           header += this.renderer.tablerow(cell);
19380     
19381           for (i = 0; i < this.token.cells.length; i++) {
19382             row = this.token.cells[i];
19383     
19384             cell = '';
19385             for (j = 0; j < row.length; j++) {
19386               cell += this.renderer.tablecell(
19387                 this.inline.output(row[j]),
19388                 { header: false, align: this.token.align[j] }
19389               );
19390             }
19391     
19392             body += this.renderer.tablerow(cell);
19393           }
19394           return this.renderer.table(header, body);
19395         }
19396         case 'blockquote_start': {
19397           var body = '';
19398     
19399           while (this.next().type !== 'blockquote_end') {
19400             body += this.tok();
19401           }
19402     
19403           return this.renderer.blockquote(body);
19404         }
19405         case 'list_start': {
19406           var body = ''
19407             , ordered = this.token.ordered;
19408     
19409           while (this.next().type !== 'list_end') {
19410             body += this.tok();
19411           }
19412     
19413           return this.renderer.list(body, ordered);
19414         }
19415         case 'list_item_start': {
19416           var body = '';
19417     
19418           while (this.next().type !== 'list_item_end') {
19419             body += this.token.type === 'text'
19420               ? this.parseText()
19421               : this.tok();
19422           }
19423     
19424           return this.renderer.listitem(body);
19425         }
19426         case 'loose_item_start': {
19427           var body = '';
19428     
19429           while (this.next().type !== 'list_item_end') {
19430             body += this.tok();
19431           }
19432     
19433           return this.renderer.listitem(body);
19434         }
19435         case 'html': {
19436           var html = !this.token.pre && !this.options.pedantic
19437             ? this.inline.output(this.token.text)
19438             : this.token.text;
19439           return this.renderer.html(html);
19440         }
19441         case 'paragraph': {
19442           return this.renderer.paragraph(this.inline.output(this.token.text));
19443         }
19444         case 'text': {
19445           return this.renderer.paragraph(this.parseText());
19446         }
19447       }
19448     };
19449   
19450     
19451     /**
19452      * Marked
19453      */
19454          /**
19455          * eval:var:marked
19456     */
19457     var marked = function (src, opt, callback) {
19458       if (callback || typeof opt === 'function') {
19459         if (!callback) {
19460           callback = opt;
19461           opt = null;
19462         }
19463     
19464         opt = merge({}, marked.defaults, opt || {});
19465     
19466         var highlight = opt.highlight
19467           , tokens
19468           , pending
19469           , i = 0;
19470     
19471         try {
19472           tokens = Lexer.lex(src, opt)
19473         } catch (e) {
19474           return callback(e);
19475         }
19476     
19477         pending = tokens.length;
19478          /**
19479          * eval:var:done
19480     */
19481         var done = function(err) {
19482           if (err) {
19483             opt.highlight = highlight;
19484             return callback(err);
19485           }
19486     
19487           var out;
19488     
19489           try {
19490             out = Parser.parse(tokens, opt);
19491           } catch (e) {
19492             err = e;
19493           }
19494     
19495           opt.highlight = highlight;
19496     
19497           return err
19498             ? callback(err)
19499             : callback(null, out);
19500         };
19501     
19502         if (!highlight || highlight.length < 3) {
19503           return done();
19504         }
19505     
19506         delete opt.highlight;
19507     
19508         if (!pending) { return done(); }
19509     
19510         for (; i < tokens.length; i++) {
19511           (function(token) {
19512             if (token.type !== 'code') {
19513               return --pending || done();
19514             }
19515             return highlight(token.text, token.lang, function(err, code) {
19516               if (err) { return done(err); }
19517               if (code == null || code === token.text) {
19518                 return --pending || done();
19519               }
19520               token.text = code;
19521               token.escaped = true;
19522               --pending || done();
19523             });
19524           })(tokens[i]);
19525         }
19526     
19527         return;
19528       }
19529       try {
19530         if (opt) { opt = merge({}, marked.defaults, opt); }
19531         return Parser.parse(Lexer.lex(src, opt), opt);
19532       } catch (e) {
19533         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19534         if ((opt || marked.defaults).silent) {
19535           return '<p>An error occured:</p><pre>'
19536             + escape(e.message + '', true)
19537             + '</pre>';
19538         }
19539         throw e;
19540       }
19541     }
19542     
19543     /**
19544      * Options
19545      */
19546     
19547     marked.options =
19548     marked.setOptions = function(opt) {
19549       merge(marked.defaults, opt);
19550       return marked;
19551     };
19552     
19553     marked.defaults = {
19554       gfm: true,
19555       tables: true,
19556       breaks: false,
19557       pedantic: false,
19558       sanitize: false,
19559       sanitizer: null,
19560       mangle: true,
19561       smartLists: false,
19562       silent: false,
19563       highlight: null,
19564       langPrefix: 'lang-',
19565       smartypants: false,
19566       headerPrefix: '',
19567       renderer: new Renderer,
19568       xhtml: false
19569     };
19570     
19571     /**
19572      * Expose
19573      */
19574     
19575     marked.Parser = Parser;
19576     marked.parser = Parser.parse;
19577     
19578     marked.Renderer = Renderer;
19579     
19580     marked.Lexer = Lexer;
19581     marked.lexer = Lexer.lex;
19582     
19583     marked.InlineLexer = InlineLexer;
19584     marked.inlineLexer = InlineLexer.output;
19585     
19586     marked.parse = marked;
19587     
19588     Roo.Markdown.marked = marked;
19589
19590 })();/*
19591  * Based on:
19592  * Ext JS Library 1.1.1
19593  * Copyright(c) 2006-2007, Ext JS, LLC.
19594  *
19595  * Originally Released Under LGPL - original licence link has changed is not relivant.
19596  *
19597  * Fork - LGPL
19598  * <script type="text/javascript">
19599  */
19600
19601
19602
19603 /*
19604  * These classes are derivatives of the similarly named classes in the YUI Library.
19605  * The original license:
19606  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19607  * Code licensed under the BSD License:
19608  * http://developer.yahoo.net/yui/license.txt
19609  */
19610
19611 (function() {
19612
19613 var Event=Roo.EventManager;
19614 var Dom=Roo.lib.Dom;
19615
19616 /**
19617  * @class Roo.dd.DragDrop
19618  * @extends Roo.util.Observable
19619  * Defines the interface and base operation of items that that can be
19620  * dragged or can be drop targets.  It was designed to be extended, overriding
19621  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19622  * Up to three html elements can be associated with a DragDrop instance:
19623  * <ul>
19624  * <li>linked element: the element that is passed into the constructor.
19625  * This is the element which defines the boundaries for interaction with
19626  * other DragDrop objects.</li>
19627  * <li>handle element(s): The drag operation only occurs if the element that
19628  * was clicked matches a handle element.  By default this is the linked
19629  * element, but there are times that you will want only a portion of the
19630  * linked element to initiate the drag operation, and the setHandleElId()
19631  * method provides a way to define this.</li>
19632  * <li>drag element: this represents the element that would be moved along
19633  * with the cursor during a drag operation.  By default, this is the linked
19634  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19635  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19636  * </li>
19637  * </ul>
19638  * This class should not be instantiated until the onload event to ensure that
19639  * the associated elements are available.
19640  * The following would define a DragDrop obj that would interact with any
19641  * other DragDrop obj in the "group1" group:
19642  * <pre>
19643  *  dd = new Roo.dd.DragDrop("div1", "group1");
19644  * </pre>
19645  * Since none of the event handlers have been implemented, nothing would
19646  * actually happen if you were to run the code above.  Normally you would
19647  * override this class or one of the default implementations, but you can
19648  * also override the methods you want on an instance of the class...
19649  * <pre>
19650  *  dd.onDragDrop = function(e, id) {
19651  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19652  *  }
19653  * </pre>
19654  * @constructor
19655  * @param {String} id of the element that is linked to this instance
19656  * @param {String} sGroup the group of related DragDrop objects
19657  * @param {object} config an object containing configurable attributes
19658  *                Valid properties for DragDrop:
19659  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19660  */
19661 Roo.dd.DragDrop = function(id, sGroup, config) {
19662     if (id) {
19663         this.init(id, sGroup, config);
19664     }
19665     
19666 };
19667
19668 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19669
19670     /**
19671      * The id of the element associated with this object.  This is what we
19672      * refer to as the "linked element" because the size and position of
19673      * this element is used to determine when the drag and drop objects have
19674      * interacted.
19675      * @property id
19676      * @type String
19677      */
19678     id: null,
19679
19680     /**
19681      * Configuration attributes passed into the constructor
19682      * @property config
19683      * @type object
19684      */
19685     config: null,
19686
19687     /**
19688      * The id of the element that will be dragged.  By default this is same
19689      * as the linked element , but could be changed to another element. Ex:
19690      * Roo.dd.DDProxy
19691      * @property dragElId
19692      * @type String
19693      * @private
19694      */
19695     dragElId: null,
19696
19697     /**
19698      * the id of the element that initiates the drag operation.  By default
19699      * this is the linked element, but could be changed to be a child of this
19700      * element.  This lets us do things like only starting the drag when the
19701      * header element within the linked html element is clicked.
19702      * @property handleElId
19703      * @type String
19704      * @private
19705      */
19706     handleElId: null,
19707
19708     /**
19709      * An associative array of HTML tags that will be ignored if clicked.
19710      * @property invalidHandleTypes
19711      * @type {string: string}
19712      */
19713     invalidHandleTypes: null,
19714
19715     /**
19716      * An associative array of ids for elements that will be ignored if clicked
19717      * @property invalidHandleIds
19718      * @type {string: string}
19719      */
19720     invalidHandleIds: null,
19721
19722     /**
19723      * An indexted array of css class names for elements that will be ignored
19724      * if clicked.
19725      * @property invalidHandleClasses
19726      * @type string[]
19727      */
19728     invalidHandleClasses: null,
19729
19730     /**
19731      * The linked element's absolute X position at the time the drag was
19732      * started
19733      * @property startPageX
19734      * @type int
19735      * @private
19736      */
19737     startPageX: 0,
19738
19739     /**
19740      * The linked element's absolute X position at the time the drag was
19741      * started
19742      * @property startPageY
19743      * @type int
19744      * @private
19745      */
19746     startPageY: 0,
19747
19748     /**
19749      * The group defines a logical collection of DragDrop objects that are
19750      * related.  Instances only get events when interacting with other
19751      * DragDrop object in the same group.  This lets us define multiple
19752      * groups using a single DragDrop subclass if we want.
19753      * @property groups
19754      * @type {string: string}
19755      */
19756     groups: null,
19757
19758     /**
19759      * Individual drag/drop instances can be locked.  This will prevent
19760      * onmousedown start drag.
19761      * @property locked
19762      * @type boolean
19763      * @private
19764      */
19765     locked: false,
19766
19767     /**
19768      * Lock this instance
19769      * @method lock
19770      */
19771     lock: function() { this.locked = true; },
19772
19773     /**
19774      * Unlock this instace
19775      * @method unlock
19776      */
19777     unlock: function() { this.locked = false; },
19778
19779     /**
19780      * By default, all insances can be a drop target.  This can be disabled by
19781      * setting isTarget to false.
19782      * @method isTarget
19783      * @type boolean
19784      */
19785     isTarget: true,
19786
19787     /**
19788      * The padding configured for this drag and drop object for calculating
19789      * the drop zone intersection with this object.
19790      * @method padding
19791      * @type int[]
19792      */
19793     padding: null,
19794
19795     /**
19796      * Cached reference to the linked element
19797      * @property _domRef
19798      * @private
19799      */
19800     _domRef: null,
19801
19802     /**
19803      * Internal typeof flag
19804      * @property __ygDragDrop
19805      * @private
19806      */
19807     __ygDragDrop: true,
19808
19809     /**
19810      * Set to true when horizontal contraints are applied
19811      * @property constrainX
19812      * @type boolean
19813      * @private
19814      */
19815     constrainX: false,
19816
19817     /**
19818      * Set to true when vertical contraints are applied
19819      * @property constrainY
19820      * @type boolean
19821      * @private
19822      */
19823     constrainY: false,
19824
19825     /**
19826      * The left constraint
19827      * @property minX
19828      * @type int
19829      * @private
19830      */
19831     minX: 0,
19832
19833     /**
19834      * The right constraint
19835      * @property maxX
19836      * @type int
19837      * @private
19838      */
19839     maxX: 0,
19840
19841     /**
19842      * The up constraint
19843      * @property minY
19844      * @type int
19845      * @type int
19846      * @private
19847      */
19848     minY: 0,
19849
19850     /**
19851      * The down constraint
19852      * @property maxY
19853      * @type int
19854      * @private
19855      */
19856     maxY: 0,
19857
19858     /**
19859      * Maintain offsets when we resetconstraints.  Set to true when you want
19860      * the position of the element relative to its parent to stay the same
19861      * when the page changes
19862      *
19863      * @property maintainOffset
19864      * @type boolean
19865      */
19866     maintainOffset: false,
19867
19868     /**
19869      * Array of pixel locations the element will snap to if we specified a
19870      * horizontal graduation/interval.  This array is generated automatically
19871      * when you define a tick interval.
19872      * @property xTicks
19873      * @type int[]
19874      */
19875     xTicks: null,
19876
19877     /**
19878      * Array of pixel locations the element will snap to if we specified a
19879      * vertical graduation/interval.  This array is generated automatically
19880      * when you define a tick interval.
19881      * @property yTicks
19882      * @type int[]
19883      */
19884     yTicks: null,
19885
19886     /**
19887      * By default the drag and drop instance will only respond to the primary
19888      * button click (left button for a right-handed mouse).  Set to true to
19889      * allow drag and drop to start with any mouse click that is propogated
19890      * by the browser
19891      * @property primaryButtonOnly
19892      * @type boolean
19893      */
19894     primaryButtonOnly: true,
19895
19896     /**
19897      * The availabe property is false until the linked dom element is accessible.
19898      * @property available
19899      * @type boolean
19900      */
19901     available: false,
19902
19903     /**
19904      * By default, drags can only be initiated if the mousedown occurs in the
19905      * region the linked element is.  This is done in part to work around a
19906      * bug in some browsers that mis-report the mousedown if the previous
19907      * mouseup happened outside of the window.  This property is set to true
19908      * if outer handles are defined.
19909      *
19910      * @property hasOuterHandles
19911      * @type boolean
19912      * @default false
19913      */
19914     hasOuterHandles: false,
19915
19916     /**
19917      * Code that executes immediately before the startDrag event
19918      * @method b4StartDrag
19919      * @private
19920      */
19921     b4StartDrag: function(x, y) { },
19922
19923     /**
19924      * Abstract method called after a drag/drop object is clicked
19925      * and the drag or mousedown time thresholds have beeen met.
19926      * @method startDrag
19927      * @param {int} X click location
19928      * @param {int} Y click location
19929      */
19930     startDrag: function(x, y) { /* override this */ },
19931
19932     /**
19933      * Code that executes immediately before the onDrag event
19934      * @method b4Drag
19935      * @private
19936      */
19937     b4Drag: function(e) { },
19938
19939     /**
19940      * Abstract method called during the onMouseMove event while dragging an
19941      * object.
19942      * @method onDrag
19943      * @param {Event} e the mousemove event
19944      */
19945     onDrag: function(e) { /* override this */ },
19946
19947     /**
19948      * Abstract method called when this element fist begins hovering over
19949      * another DragDrop obj
19950      * @method onDragEnter
19951      * @param {Event} e the mousemove event
19952      * @param {String|DragDrop[]} id In POINT mode, the element
19953      * id this is hovering over.  In INTERSECT mode, an array of one or more
19954      * dragdrop items being hovered over.
19955      */
19956     onDragEnter: function(e, id) { /* override this */ },
19957
19958     /**
19959      * Code that executes immediately before the onDragOver event
19960      * @method b4DragOver
19961      * @private
19962      */
19963     b4DragOver: function(e) { },
19964
19965     /**
19966      * Abstract method called when this element is hovering over another
19967      * DragDrop obj
19968      * @method onDragOver
19969      * @param {Event} e the mousemove event
19970      * @param {String|DragDrop[]} id In POINT mode, the element
19971      * id this is hovering over.  In INTERSECT mode, an array of dd items
19972      * being hovered over.
19973      */
19974     onDragOver: function(e, id) { /* override this */ },
19975
19976     /**
19977      * Code that executes immediately before the onDragOut event
19978      * @method b4DragOut
19979      * @private
19980      */
19981     b4DragOut: function(e) { },
19982
19983     /**
19984      * Abstract method called when we are no longer hovering over an element
19985      * @method onDragOut
19986      * @param {Event} e the mousemove event
19987      * @param {String|DragDrop[]} id In POINT mode, the element
19988      * id this was hovering over.  In INTERSECT mode, an array of dd items
19989      * that the mouse is no longer over.
19990      */
19991     onDragOut: function(e, id) { /* override this */ },
19992
19993     /**
19994      * Code that executes immediately before the onDragDrop event
19995      * @method b4DragDrop
19996      * @private
19997      */
19998     b4DragDrop: function(e) { },
19999
20000     /**
20001      * Abstract method called when this item is dropped on another DragDrop
20002      * obj
20003      * @method onDragDrop
20004      * @param {Event} e the mouseup event
20005      * @param {String|DragDrop[]} id In POINT mode, the element
20006      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20007      * was dropped on.
20008      */
20009     onDragDrop: function(e, id) { /* override this */ },
20010
20011     /**
20012      * Abstract method called when this item is dropped on an area with no
20013      * drop target
20014      * @method onInvalidDrop
20015      * @param {Event} e the mouseup event
20016      */
20017     onInvalidDrop: function(e) { /* override this */ },
20018
20019     /**
20020      * Code that executes immediately before the endDrag event
20021      * @method b4EndDrag
20022      * @private
20023      */
20024     b4EndDrag: function(e) { },
20025
20026     /**
20027      * Fired when we are done dragging the object
20028      * @method endDrag
20029      * @param {Event} e the mouseup event
20030      */
20031     endDrag: function(e) { /* override this */ },
20032
20033     /**
20034      * Code executed immediately before the onMouseDown event
20035      * @method b4MouseDown
20036      * @param {Event} e the mousedown event
20037      * @private
20038      */
20039     b4MouseDown: function(e) {  },
20040
20041     /**
20042      * Event handler that fires when a drag/drop obj gets a mousedown
20043      * @method onMouseDown
20044      * @param {Event} e the mousedown event
20045      */
20046     onMouseDown: function(e) { /* override this */ },
20047
20048     /**
20049      * Event handler that fires when a drag/drop obj gets a mouseup
20050      * @method onMouseUp
20051      * @param {Event} e the mouseup event
20052      */
20053     onMouseUp: function(e) { /* override this */ },
20054
20055     /**
20056      * Override the onAvailable method to do what is needed after the initial
20057      * position was determined.
20058      * @method onAvailable
20059      */
20060     onAvailable: function () {
20061     },
20062
20063     /*
20064      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20065      * @type Object
20066      */
20067     defaultPadding : {left:0, right:0, top:0, bottom:0},
20068
20069     /*
20070      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20071  *
20072  * Usage:
20073  <pre><code>
20074  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20075                 { dragElId: "existingProxyDiv" });
20076  dd.startDrag = function(){
20077      this.constrainTo("parent-id");
20078  };
20079  </code></pre>
20080  * Or you can initalize it using the {@link Roo.Element} object:
20081  <pre><code>
20082  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20083      startDrag : function(){
20084          this.constrainTo("parent-id");
20085      }
20086  });
20087  </code></pre>
20088      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20089      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20090      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20091      * an object containing the sides to pad. For example: {right:10, bottom:10}
20092      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20093      */
20094     constrainTo : function(constrainTo, pad, inContent){
20095         if(typeof pad == "number"){
20096             pad = {left: pad, right:pad, top:pad, bottom:pad};
20097         }
20098         pad = pad || this.defaultPadding;
20099         var b = Roo.get(this.getEl()).getBox();
20100         var ce = Roo.get(constrainTo);
20101         var s = ce.getScroll();
20102         var c, cd = ce.dom;
20103         if(cd == document.body){
20104             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20105         }else{
20106             xy = ce.getXY();
20107             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20108         }
20109
20110
20111         var topSpace = b.y - c.y;
20112         var leftSpace = b.x - c.x;
20113
20114         this.resetConstraints();
20115         this.setXConstraint(leftSpace - (pad.left||0), // left
20116                 c.width - leftSpace - b.width - (pad.right||0) //right
20117         );
20118         this.setYConstraint(topSpace - (pad.top||0), //top
20119                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20120         );
20121     },
20122
20123     /**
20124      * Returns a reference to the linked element
20125      * @method getEl
20126      * @return {HTMLElement} the html element
20127      */
20128     getEl: function() {
20129         if (!this._domRef) {
20130             this._domRef = Roo.getDom(this.id);
20131         }
20132
20133         return this._domRef;
20134     },
20135
20136     /**
20137      * Returns a reference to the actual element to drag.  By default this is
20138      * the same as the html element, but it can be assigned to another
20139      * element. An example of this can be found in Roo.dd.DDProxy
20140      * @method getDragEl
20141      * @return {HTMLElement} the html element
20142      */
20143     getDragEl: function() {
20144         return Roo.getDom(this.dragElId);
20145     },
20146
20147     /**
20148      * Sets up the DragDrop object.  Must be called in the constructor of any
20149      * Roo.dd.DragDrop subclass
20150      * @method init
20151      * @param id the id of the linked element
20152      * @param {String} sGroup the group of related items
20153      * @param {object} config configuration attributes
20154      */
20155     init: function(id, sGroup, config) {
20156         this.initTarget(id, sGroup, config);
20157         if (!Roo.isTouch) {
20158             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20159         }
20160         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20161         // Event.on(this.id, "selectstart", Event.preventDefault);
20162     },
20163
20164     /**
20165      * Initializes Targeting functionality only... the object does not
20166      * get a mousedown handler.
20167      * @method initTarget
20168      * @param id the id of the linked element
20169      * @param {String} sGroup the group of related items
20170      * @param {object} config configuration attributes
20171      */
20172     initTarget: function(id, sGroup, config) {
20173
20174         // configuration attributes
20175         this.config = config || {};
20176
20177         // create a local reference to the drag and drop manager
20178         this.DDM = Roo.dd.DDM;
20179         // initialize the groups array
20180         this.groups = {};
20181
20182         // assume that we have an element reference instead of an id if the
20183         // parameter is not a string
20184         if (typeof id !== "string") {
20185             id = Roo.id(id);
20186         }
20187
20188         // set the id
20189         this.id = id;
20190
20191         // add to an interaction group
20192         this.addToGroup((sGroup) ? sGroup : "default");
20193
20194         // We don't want to register this as the handle with the manager
20195         // so we just set the id rather than calling the setter.
20196         this.handleElId = id;
20197
20198         // the linked element is the element that gets dragged by default
20199         this.setDragElId(id);
20200
20201         // by default, clicked anchors will not start drag operations.
20202         this.invalidHandleTypes = { A: "A" };
20203         this.invalidHandleIds = {};
20204         this.invalidHandleClasses = [];
20205
20206         this.applyConfig();
20207
20208         this.handleOnAvailable();
20209     },
20210
20211     /**
20212      * Applies the configuration parameters that were passed into the constructor.
20213      * This is supposed to happen at each level through the inheritance chain.  So
20214      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20215      * DragDrop in order to get all of the parameters that are available in
20216      * each object.
20217      * @method applyConfig
20218      */
20219     applyConfig: function() {
20220
20221         // configurable properties:
20222         //    padding, isTarget, maintainOffset, primaryButtonOnly
20223         this.padding           = this.config.padding || [0, 0, 0, 0];
20224         this.isTarget          = (this.config.isTarget !== false);
20225         this.maintainOffset    = (this.config.maintainOffset);
20226         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20227
20228     },
20229
20230     /**
20231      * Executed when the linked element is available
20232      * @method handleOnAvailable
20233      * @private
20234      */
20235     handleOnAvailable: function() {
20236         this.available = true;
20237         this.resetConstraints();
20238         this.onAvailable();
20239     },
20240
20241      /**
20242      * Configures the padding for the target zone in px.  Effectively expands
20243      * (or reduces) the virtual object size for targeting calculations.
20244      * Supports css-style shorthand; if only one parameter is passed, all sides
20245      * will have that padding, and if only two are passed, the top and bottom
20246      * will have the first param, the left and right the second.
20247      * @method setPadding
20248      * @param {int} iTop    Top pad
20249      * @param {int} iRight  Right pad
20250      * @param {int} iBot    Bot pad
20251      * @param {int} iLeft   Left pad
20252      */
20253     setPadding: function(iTop, iRight, iBot, iLeft) {
20254         // this.padding = [iLeft, iRight, iTop, iBot];
20255         if (!iRight && 0 !== iRight) {
20256             this.padding = [iTop, iTop, iTop, iTop];
20257         } else if (!iBot && 0 !== iBot) {
20258             this.padding = [iTop, iRight, iTop, iRight];
20259         } else {
20260             this.padding = [iTop, iRight, iBot, iLeft];
20261         }
20262     },
20263
20264     /**
20265      * Stores the initial placement of the linked element.
20266      * @method setInitialPosition
20267      * @param {int} diffX   the X offset, default 0
20268      * @param {int} diffY   the Y offset, default 0
20269      */
20270     setInitPosition: function(diffX, diffY) {
20271         var el = this.getEl();
20272
20273         if (!this.DDM.verifyEl(el)) {
20274             return;
20275         }
20276
20277         var dx = diffX || 0;
20278         var dy = diffY || 0;
20279
20280         var p = Dom.getXY( el );
20281
20282         this.initPageX = p[0] - dx;
20283         this.initPageY = p[1] - dy;
20284
20285         this.lastPageX = p[0];
20286         this.lastPageY = p[1];
20287
20288
20289         this.setStartPosition(p);
20290     },
20291
20292     /**
20293      * Sets the start position of the element.  This is set when the obj
20294      * is initialized, the reset when a drag is started.
20295      * @method setStartPosition
20296      * @param pos current position (from previous lookup)
20297      * @private
20298      */
20299     setStartPosition: function(pos) {
20300         var p = pos || Dom.getXY( this.getEl() );
20301         this.deltaSetXY = null;
20302
20303         this.startPageX = p[0];
20304         this.startPageY = p[1];
20305     },
20306
20307     /**
20308      * Add this instance to a group of related drag/drop objects.  All
20309      * instances belong to at least one group, and can belong to as many
20310      * groups as needed.
20311      * @method addToGroup
20312      * @param sGroup {string} the name of the group
20313      */
20314     addToGroup: function(sGroup) {
20315         this.groups[sGroup] = true;
20316         this.DDM.regDragDrop(this, sGroup);
20317     },
20318
20319     /**
20320      * Remove's this instance from the supplied interaction group
20321      * @method removeFromGroup
20322      * @param {string}  sGroup  The group to drop
20323      */
20324     removeFromGroup: function(sGroup) {
20325         if (this.groups[sGroup]) {
20326             delete this.groups[sGroup];
20327         }
20328
20329         this.DDM.removeDDFromGroup(this, sGroup);
20330     },
20331
20332     /**
20333      * Allows you to specify that an element other than the linked element
20334      * will be moved with the cursor during a drag
20335      * @method setDragElId
20336      * @param id {string} the id of the element that will be used to initiate the drag
20337      */
20338     setDragElId: function(id) {
20339         this.dragElId = id;
20340     },
20341
20342     /**
20343      * Allows you to specify a child of the linked element that should be
20344      * used to initiate the drag operation.  An example of this would be if
20345      * you have a content div with text and links.  Clicking anywhere in the
20346      * content area would normally start the drag operation.  Use this method
20347      * to specify that an element inside of the content div is the element
20348      * that starts the drag operation.
20349      * @method setHandleElId
20350      * @param id {string} the id of the element that will be used to
20351      * initiate the drag.
20352      */
20353     setHandleElId: function(id) {
20354         if (typeof id !== "string") {
20355             id = Roo.id(id);
20356         }
20357         this.handleElId = id;
20358         this.DDM.regHandle(this.id, id);
20359     },
20360
20361     /**
20362      * Allows you to set an element outside of the linked element as a drag
20363      * handle
20364      * @method setOuterHandleElId
20365      * @param id the id of the element that will be used to initiate the drag
20366      */
20367     setOuterHandleElId: function(id) {
20368         if (typeof id !== "string") {
20369             id = Roo.id(id);
20370         }
20371         Event.on(id, "mousedown",
20372                 this.handleMouseDown, this);
20373         this.setHandleElId(id);
20374
20375         this.hasOuterHandles = true;
20376     },
20377
20378     /**
20379      * Remove all drag and drop hooks for this element
20380      * @method unreg
20381      */
20382     unreg: function() {
20383         Event.un(this.id, "mousedown",
20384                 this.handleMouseDown);
20385         Event.un(this.id, "touchstart",
20386                 this.handleMouseDown);
20387         this._domRef = null;
20388         this.DDM._remove(this);
20389     },
20390
20391     destroy : function(){
20392         this.unreg();
20393     },
20394
20395     /**
20396      * Returns true if this instance is locked, or the drag drop mgr is locked
20397      * (meaning that all drag/drop is disabled on the page.)
20398      * @method isLocked
20399      * @return {boolean} true if this obj or all drag/drop is locked, else
20400      * false
20401      */
20402     isLocked: function() {
20403         return (this.DDM.isLocked() || this.locked);
20404     },
20405
20406     /**
20407      * Fired when this object is clicked
20408      * @method handleMouseDown
20409      * @param {Event} e
20410      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20411      * @private
20412      */
20413     handleMouseDown: function(e, oDD){
20414      
20415         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20416             //Roo.log('not touch/ button !=0');
20417             return;
20418         }
20419         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20420             return; // double touch..
20421         }
20422         
20423
20424         if (this.isLocked()) {
20425             //Roo.log('locked');
20426             return;
20427         }
20428
20429         this.DDM.refreshCache(this.groups);
20430 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20431         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20432         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20433             //Roo.log('no outer handes or not over target');
20434                 // do nothing.
20435         } else {
20436 //            Roo.log('check validator');
20437             if (this.clickValidator(e)) {
20438 //                Roo.log('validate success');
20439                 // set the initial element position
20440                 this.setStartPosition();
20441
20442
20443                 this.b4MouseDown(e);
20444                 this.onMouseDown(e);
20445
20446                 this.DDM.handleMouseDown(e, this);
20447
20448                 this.DDM.stopEvent(e);
20449             } else {
20450
20451
20452             }
20453         }
20454     },
20455
20456     clickValidator: function(e) {
20457         var target = e.getTarget();
20458         return ( this.isValidHandleChild(target) &&
20459                     (this.id == this.handleElId ||
20460                         this.DDM.handleWasClicked(target, this.id)) );
20461     },
20462
20463     /**
20464      * Allows you to specify a tag name that should not start a drag operation
20465      * when clicked.  This is designed to facilitate embedding links within a
20466      * drag handle that do something other than start the drag.
20467      * @method addInvalidHandleType
20468      * @param {string} tagName the type of element to exclude
20469      */
20470     addInvalidHandleType: function(tagName) {
20471         var type = tagName.toUpperCase();
20472         this.invalidHandleTypes[type] = type;
20473     },
20474
20475     /**
20476      * Lets you to specify an element id for a child of a drag handle
20477      * that should not initiate a drag
20478      * @method addInvalidHandleId
20479      * @param {string} id the element id of the element you wish to ignore
20480      */
20481     addInvalidHandleId: function(id) {
20482         if (typeof id !== "string") {
20483             id = Roo.id(id);
20484         }
20485         this.invalidHandleIds[id] = id;
20486     },
20487
20488     /**
20489      * Lets you specify a css class of elements that will not initiate a drag
20490      * @method addInvalidHandleClass
20491      * @param {string} cssClass the class of the elements you wish to ignore
20492      */
20493     addInvalidHandleClass: function(cssClass) {
20494         this.invalidHandleClasses.push(cssClass);
20495     },
20496
20497     /**
20498      * Unsets an excluded tag name set by addInvalidHandleType
20499      * @method removeInvalidHandleType
20500      * @param {string} tagName the type of element to unexclude
20501      */
20502     removeInvalidHandleType: function(tagName) {
20503         var type = tagName.toUpperCase();
20504         // this.invalidHandleTypes[type] = null;
20505         delete this.invalidHandleTypes[type];
20506     },
20507
20508     /**
20509      * Unsets an invalid handle id
20510      * @method removeInvalidHandleId
20511      * @param {string} id the id of the element to re-enable
20512      */
20513     removeInvalidHandleId: function(id) {
20514         if (typeof id !== "string") {
20515             id = Roo.id(id);
20516         }
20517         delete this.invalidHandleIds[id];
20518     },
20519
20520     /**
20521      * Unsets an invalid css class
20522      * @method removeInvalidHandleClass
20523      * @param {string} cssClass the class of the element(s) you wish to
20524      * re-enable
20525      */
20526     removeInvalidHandleClass: function(cssClass) {
20527         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20528             if (this.invalidHandleClasses[i] == cssClass) {
20529                 delete this.invalidHandleClasses[i];
20530             }
20531         }
20532     },
20533
20534     /**
20535      * Checks the tag exclusion list to see if this click should be ignored
20536      * @method isValidHandleChild
20537      * @param {HTMLElement} node the HTMLElement to evaluate
20538      * @return {boolean} true if this is a valid tag type, false if not
20539      */
20540     isValidHandleChild: function(node) {
20541
20542         var valid = true;
20543         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20544         var nodeName;
20545         try {
20546             nodeName = node.nodeName.toUpperCase();
20547         } catch(e) {
20548             nodeName = node.nodeName;
20549         }
20550         valid = valid && !this.invalidHandleTypes[nodeName];
20551         valid = valid && !this.invalidHandleIds[node.id];
20552
20553         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20554             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20555         }
20556
20557
20558         return valid;
20559
20560     },
20561
20562     /**
20563      * Create the array of horizontal tick marks if an interval was specified
20564      * in setXConstraint().
20565      * @method setXTicks
20566      * @private
20567      */
20568     setXTicks: function(iStartX, iTickSize) {
20569         this.xTicks = [];
20570         this.xTickSize = iTickSize;
20571
20572         var tickMap = {};
20573
20574         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20575             if (!tickMap[i]) {
20576                 this.xTicks[this.xTicks.length] = i;
20577                 tickMap[i] = true;
20578             }
20579         }
20580
20581         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20582             if (!tickMap[i]) {
20583                 this.xTicks[this.xTicks.length] = i;
20584                 tickMap[i] = true;
20585             }
20586         }
20587
20588         this.xTicks.sort(this.DDM.numericSort) ;
20589     },
20590
20591     /**
20592      * Create the array of vertical tick marks if an interval was specified in
20593      * setYConstraint().
20594      * @method setYTicks
20595      * @private
20596      */
20597     setYTicks: function(iStartY, iTickSize) {
20598         this.yTicks = [];
20599         this.yTickSize = iTickSize;
20600
20601         var tickMap = {};
20602
20603         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20604             if (!tickMap[i]) {
20605                 this.yTicks[this.yTicks.length] = i;
20606                 tickMap[i] = true;
20607             }
20608         }
20609
20610         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20611             if (!tickMap[i]) {
20612                 this.yTicks[this.yTicks.length] = i;
20613                 tickMap[i] = true;
20614             }
20615         }
20616
20617         this.yTicks.sort(this.DDM.numericSort) ;
20618     },
20619
20620     /**
20621      * By default, the element can be dragged any place on the screen.  Use
20622      * this method to limit the horizontal travel of the element.  Pass in
20623      * 0,0 for the parameters if you want to lock the drag to the y axis.
20624      * @method setXConstraint
20625      * @param {int} iLeft the number of pixels the element can move to the left
20626      * @param {int} iRight the number of pixels the element can move to the
20627      * right
20628      * @param {int} iTickSize optional parameter for specifying that the
20629      * element
20630      * should move iTickSize pixels at a time.
20631      */
20632     setXConstraint: function(iLeft, iRight, iTickSize) {
20633         this.leftConstraint = iLeft;
20634         this.rightConstraint = iRight;
20635
20636         this.minX = this.initPageX - iLeft;
20637         this.maxX = this.initPageX + iRight;
20638         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20639
20640         this.constrainX = true;
20641     },
20642
20643     /**
20644      * Clears any constraints applied to this instance.  Also clears ticks
20645      * since they can't exist independent of a constraint at this time.
20646      * @method clearConstraints
20647      */
20648     clearConstraints: function() {
20649         this.constrainX = false;
20650         this.constrainY = false;
20651         this.clearTicks();
20652     },
20653
20654     /**
20655      * Clears any tick interval defined for this instance
20656      * @method clearTicks
20657      */
20658     clearTicks: function() {
20659         this.xTicks = null;
20660         this.yTicks = null;
20661         this.xTickSize = 0;
20662         this.yTickSize = 0;
20663     },
20664
20665     /**
20666      * By default, the element can be dragged any place on the screen.  Set
20667      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20668      * parameters if you want to lock the drag to the x axis.
20669      * @method setYConstraint
20670      * @param {int} iUp the number of pixels the element can move up
20671      * @param {int} iDown the number of pixels the element can move down
20672      * @param {int} iTickSize optional parameter for specifying that the
20673      * element should move iTickSize pixels at a time.
20674      */
20675     setYConstraint: function(iUp, iDown, iTickSize) {
20676         this.topConstraint = iUp;
20677         this.bottomConstraint = iDown;
20678
20679         this.minY = this.initPageY - iUp;
20680         this.maxY = this.initPageY + iDown;
20681         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20682
20683         this.constrainY = true;
20684
20685     },
20686
20687     /**
20688      * resetConstraints must be called if you manually reposition a dd element.
20689      * @method resetConstraints
20690      * @param {boolean} maintainOffset
20691      */
20692     resetConstraints: function() {
20693
20694
20695         // Maintain offsets if necessary
20696         if (this.initPageX || this.initPageX === 0) {
20697             // figure out how much this thing has moved
20698             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20699             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20700
20701             this.setInitPosition(dx, dy);
20702
20703         // This is the first time we have detected the element's position
20704         } else {
20705             this.setInitPosition();
20706         }
20707
20708         if (this.constrainX) {
20709             this.setXConstraint( this.leftConstraint,
20710                                  this.rightConstraint,
20711                                  this.xTickSize        );
20712         }
20713
20714         if (this.constrainY) {
20715             this.setYConstraint( this.topConstraint,
20716                                  this.bottomConstraint,
20717                                  this.yTickSize         );
20718         }
20719     },
20720
20721     /**
20722      * Normally the drag element is moved pixel by pixel, but we can specify
20723      * that it move a number of pixels at a time.  This method resolves the
20724      * location when we have it set up like this.
20725      * @method getTick
20726      * @param {int} val where we want to place the object
20727      * @param {int[]} tickArray sorted array of valid points
20728      * @return {int} the closest tick
20729      * @private
20730      */
20731     getTick: function(val, tickArray) {
20732
20733         if (!tickArray) {
20734             // If tick interval is not defined, it is effectively 1 pixel,
20735             // so we return the value passed to us.
20736             return val;
20737         } else if (tickArray[0] >= val) {
20738             // The value is lower than the first tick, so we return the first
20739             // tick.
20740             return tickArray[0];
20741         } else {
20742             for (var i=0, len=tickArray.length; i<len; ++i) {
20743                 var next = i + 1;
20744                 if (tickArray[next] && tickArray[next] >= val) {
20745                     var diff1 = val - tickArray[i];
20746                     var diff2 = tickArray[next] - val;
20747                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20748                 }
20749             }
20750
20751             // The value is larger than the last tick, so we return the last
20752             // tick.
20753             return tickArray[tickArray.length - 1];
20754         }
20755     },
20756
20757     /**
20758      * toString method
20759      * @method toString
20760      * @return {string} string representation of the dd obj
20761      */
20762     toString: function() {
20763         return ("DragDrop " + this.id);
20764     }
20765
20766 });
20767
20768 })();
20769 /*
20770  * Based on:
20771  * Ext JS Library 1.1.1
20772  * Copyright(c) 2006-2007, Ext JS, LLC.
20773  *
20774  * Originally Released Under LGPL - original licence link has changed is not relivant.
20775  *
20776  * Fork - LGPL
20777  * <script type="text/javascript">
20778  */
20779
20780
20781 /**
20782  * The drag and drop utility provides a framework for building drag and drop
20783  * applications.  In addition to enabling drag and drop for specific elements,
20784  * the drag and drop elements are tracked by the manager class, and the
20785  * interactions between the various elements are tracked during the drag and
20786  * the implementing code is notified about these important moments.
20787  */
20788
20789 // Only load the library once.  Rewriting the manager class would orphan
20790 // existing drag and drop instances.
20791 if (!Roo.dd.DragDropMgr) {
20792
20793 /**
20794  * @class Roo.dd.DragDropMgr
20795  * DragDropMgr is a singleton that tracks the element interaction for
20796  * all DragDrop items in the window.  Generally, you will not call
20797  * this class directly, but it does have helper methods that could
20798  * be useful in your DragDrop implementations.
20799  * @static
20800  */
20801 Roo.dd.DragDropMgr = function() {
20802
20803     var Event = Roo.EventManager;
20804
20805     return {
20806
20807         /**
20808          * Two dimensional Array of registered DragDrop objects.  The first
20809          * dimension is the DragDrop item group, the second the DragDrop
20810          * object.
20811          * @property ids
20812          * @type {string: string}
20813          * @private
20814          * @static
20815          */
20816         ids: {},
20817
20818         /**
20819          * Array of element ids defined as drag handles.  Used to determine
20820          * if the element that generated the mousedown event is actually the
20821          * handle and not the html element itself.
20822          * @property handleIds
20823          * @type {string: string}
20824          * @private
20825          * @static
20826          */
20827         handleIds: {},
20828
20829         /**
20830          * the DragDrop object that is currently being dragged
20831          * @property dragCurrent
20832          * @type DragDrop
20833          * @private
20834          * @static
20835          **/
20836         dragCurrent: null,
20837
20838         /**
20839          * the DragDrop object(s) that are being hovered over
20840          * @property dragOvers
20841          * @type Array
20842          * @private
20843          * @static
20844          */
20845         dragOvers: {},
20846
20847         /**
20848          * the X distance between the cursor and the object being dragged
20849          * @property deltaX
20850          * @type int
20851          * @private
20852          * @static
20853          */
20854         deltaX: 0,
20855
20856         /**
20857          * the Y distance between the cursor and the object being dragged
20858          * @property deltaY
20859          * @type int
20860          * @private
20861          * @static
20862          */
20863         deltaY: 0,
20864
20865         /**
20866          * Flag to determine if we should prevent the default behavior of the
20867          * events we define. By default this is true, but this can be set to
20868          * false if you need the default behavior (not recommended)
20869          * @property preventDefault
20870          * @type boolean
20871          * @static
20872          */
20873         preventDefault: true,
20874
20875         /**
20876          * Flag to determine if we should stop the propagation of the events
20877          * we generate. This is true by default but you may want to set it to
20878          * false if the html element contains other features that require the
20879          * mouse click.
20880          * @property stopPropagation
20881          * @type boolean
20882          * @static
20883          */
20884         stopPropagation: true,
20885
20886         /**
20887          * Internal flag that is set to true when drag and drop has been
20888          * intialized
20889          * @property initialized
20890          * @private
20891          * @static
20892          */
20893         initalized: false,
20894
20895         /**
20896          * All drag and drop can be disabled.
20897          * @property locked
20898          * @private
20899          * @static
20900          */
20901         locked: false,
20902
20903         /**
20904          * Called the first time an element is registered.
20905          * @method init
20906          * @private
20907          * @static
20908          */
20909         init: function() {
20910             this.initialized = true;
20911         },
20912
20913         /**
20914          * In point mode, drag and drop interaction is defined by the
20915          * location of the cursor during the drag/drop
20916          * @property POINT
20917          * @type int
20918          * @static
20919          */
20920         POINT: 0,
20921
20922         /**
20923          * In intersect mode, drag and drop interactio nis defined by the
20924          * overlap of two or more drag and drop objects.
20925          * @property INTERSECT
20926          * @type int
20927          * @static
20928          */
20929         INTERSECT: 1,
20930
20931         /**
20932          * The current drag and drop mode.  Default: POINT
20933          * @property mode
20934          * @type int
20935          * @static
20936          */
20937         mode: 0,
20938
20939         /**
20940          * Runs method on all drag and drop objects
20941          * @method _execOnAll
20942          * @private
20943          * @static
20944          */
20945         _execOnAll: function(sMethod, args) {
20946             for (var i in this.ids) {
20947                 for (var j in this.ids[i]) {
20948                     var oDD = this.ids[i][j];
20949                     if (! this.isTypeOfDD(oDD)) {
20950                         continue;
20951                     }
20952                     oDD[sMethod].apply(oDD, args);
20953                 }
20954             }
20955         },
20956
20957         /**
20958          * Drag and drop initialization.  Sets up the global event handlers
20959          * @method _onLoad
20960          * @private
20961          * @static
20962          */
20963         _onLoad: function() {
20964
20965             this.init();
20966
20967             if (!Roo.isTouch) {
20968                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20969                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20970             }
20971             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20972             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20973             
20974             Event.on(window,   "unload",    this._onUnload, this, true);
20975             Event.on(window,   "resize",    this._onResize, this, true);
20976             // Event.on(window,   "mouseout",    this._test);
20977
20978         },
20979
20980         /**
20981          * Reset constraints on all drag and drop objs
20982          * @method _onResize
20983          * @private
20984          * @static
20985          */
20986         _onResize: function(e) {
20987             this._execOnAll("resetConstraints", []);
20988         },
20989
20990         /**
20991          * Lock all drag and drop functionality
20992          * @method lock
20993          * @static
20994          */
20995         lock: function() { this.locked = true; },
20996
20997         /**
20998          * Unlock all drag and drop functionality
20999          * @method unlock
21000          * @static
21001          */
21002         unlock: function() { this.locked = false; },
21003
21004         /**
21005          * Is drag and drop locked?
21006          * @method isLocked
21007          * @return {boolean} True if drag and drop is locked, false otherwise.
21008          * @static
21009          */
21010         isLocked: function() { return this.locked; },
21011
21012         /**
21013          * Location cache that is set for all drag drop objects when a drag is
21014          * initiated, cleared when the drag is finished.
21015          * @property locationCache
21016          * @private
21017          * @static
21018          */
21019         locationCache: {},
21020
21021         /**
21022          * Set useCache to false if you want to force object the lookup of each
21023          * drag and drop linked element constantly during a drag.
21024          * @property useCache
21025          * @type boolean
21026          * @static
21027          */
21028         useCache: true,
21029
21030         /**
21031          * The number of pixels that the mouse needs to move after the
21032          * mousedown before the drag is initiated.  Default=3;
21033          * @property clickPixelThresh
21034          * @type int
21035          * @static
21036          */
21037         clickPixelThresh: 3,
21038
21039         /**
21040          * The number of milliseconds after the mousedown event to initiate the
21041          * drag if we don't get a mouseup event. Default=1000
21042          * @property clickTimeThresh
21043          * @type int
21044          * @static
21045          */
21046         clickTimeThresh: 350,
21047
21048         /**
21049          * Flag that indicates that either the drag pixel threshold or the
21050          * mousdown time threshold has been met
21051          * @property dragThreshMet
21052          * @type boolean
21053          * @private
21054          * @static
21055          */
21056         dragThreshMet: false,
21057
21058         /**
21059          * Timeout used for the click time threshold
21060          * @property clickTimeout
21061          * @type Object
21062          * @private
21063          * @static
21064          */
21065         clickTimeout: null,
21066
21067         /**
21068          * The X position of the mousedown event stored for later use when a
21069          * drag threshold is met.
21070          * @property startX
21071          * @type int
21072          * @private
21073          * @static
21074          */
21075         startX: 0,
21076
21077         /**
21078          * The Y position of the mousedown event stored for later use when a
21079          * drag threshold is met.
21080          * @property startY
21081          * @type int
21082          * @private
21083          * @static
21084          */
21085         startY: 0,
21086
21087         /**
21088          * Each DragDrop instance must be registered with the DragDropMgr.
21089          * This is executed in DragDrop.init()
21090          * @method regDragDrop
21091          * @param {DragDrop} oDD the DragDrop object to register
21092          * @param {String} sGroup the name of the group this element belongs to
21093          * @static
21094          */
21095         regDragDrop: function(oDD, sGroup) {
21096             if (!this.initialized) { this.init(); }
21097
21098             if (!this.ids[sGroup]) {
21099                 this.ids[sGroup] = {};
21100             }
21101             this.ids[sGroup][oDD.id] = oDD;
21102         },
21103
21104         /**
21105          * Removes the supplied dd instance from the supplied group. Executed
21106          * by DragDrop.removeFromGroup, so don't call this function directly.
21107          * @method removeDDFromGroup
21108          * @private
21109          * @static
21110          */
21111         removeDDFromGroup: function(oDD, sGroup) {
21112             if (!this.ids[sGroup]) {
21113                 this.ids[sGroup] = {};
21114             }
21115
21116             var obj = this.ids[sGroup];
21117             if (obj && obj[oDD.id]) {
21118                 delete obj[oDD.id];
21119             }
21120         },
21121
21122         /**
21123          * Unregisters a drag and drop item.  This is executed in
21124          * DragDrop.unreg, use that method instead of calling this directly.
21125          * @method _remove
21126          * @private
21127          * @static
21128          */
21129         _remove: function(oDD) {
21130             for (var g in oDD.groups) {
21131                 if (g && this.ids[g][oDD.id]) {
21132                     delete this.ids[g][oDD.id];
21133                 }
21134             }
21135             delete this.handleIds[oDD.id];
21136         },
21137
21138         /**
21139          * Each DragDrop handle element must be registered.  This is done
21140          * automatically when executing DragDrop.setHandleElId()
21141          * @method regHandle
21142          * @param {String} sDDId the DragDrop id this element is a handle for
21143          * @param {String} sHandleId the id of the element that is the drag
21144          * handle
21145          * @static
21146          */
21147         regHandle: function(sDDId, sHandleId) {
21148             if (!this.handleIds[sDDId]) {
21149                 this.handleIds[sDDId] = {};
21150             }
21151             this.handleIds[sDDId][sHandleId] = sHandleId;
21152         },
21153
21154         /**
21155          * Utility function to determine if a given element has been
21156          * registered as a drag drop item.
21157          * @method isDragDrop
21158          * @param {String} id the element id to check
21159          * @return {boolean} true if this element is a DragDrop item,
21160          * false otherwise
21161          * @static
21162          */
21163         isDragDrop: function(id) {
21164             return ( this.getDDById(id) ) ? true : false;
21165         },
21166
21167         /**
21168          * Returns the drag and drop instances that are in all groups the
21169          * passed in instance belongs to.
21170          * @method getRelated
21171          * @param {DragDrop} p_oDD the obj to get related data for
21172          * @param {boolean} bTargetsOnly if true, only return targetable objs
21173          * @return {DragDrop[]} the related instances
21174          * @static
21175          */
21176         getRelated: function(p_oDD, bTargetsOnly) {
21177             var oDDs = [];
21178             for (var i in p_oDD.groups) {
21179                 for (j in this.ids[i]) {
21180                     var dd = this.ids[i][j];
21181                     if (! this.isTypeOfDD(dd)) {
21182                         continue;
21183                     }
21184                     if (!bTargetsOnly || dd.isTarget) {
21185                         oDDs[oDDs.length] = dd;
21186                     }
21187                 }
21188             }
21189
21190             return oDDs;
21191         },
21192
21193         /**
21194          * Returns true if the specified dd target is a legal target for
21195          * the specifice drag obj
21196          * @method isLegalTarget
21197          * @param {DragDrop} the drag obj
21198          * @param {DragDrop} the target
21199          * @return {boolean} true if the target is a legal target for the
21200          * dd obj
21201          * @static
21202          */
21203         isLegalTarget: function (oDD, oTargetDD) {
21204             var targets = this.getRelated(oDD, true);
21205             for (var i=0, len=targets.length;i<len;++i) {
21206                 if (targets[i].id == oTargetDD.id) {
21207                     return true;
21208                 }
21209             }
21210
21211             return false;
21212         },
21213
21214         /**
21215          * My goal is to be able to transparently determine if an object is
21216          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21217          * returns "object", oDD.constructor.toString() always returns
21218          * "DragDrop" and not the name of the subclass.  So for now it just
21219          * evaluates a well-known variable in DragDrop.
21220          * @method isTypeOfDD
21221          * @param {Object} the object to evaluate
21222          * @return {boolean} true if typeof oDD = DragDrop
21223          * @static
21224          */
21225         isTypeOfDD: function (oDD) {
21226             return (oDD && oDD.__ygDragDrop);
21227         },
21228
21229         /**
21230          * Utility function to determine if a given element has been
21231          * registered as a drag drop handle for the given Drag Drop object.
21232          * @method isHandle
21233          * @param {String} id the element id to check
21234          * @return {boolean} true if this element is a DragDrop handle, false
21235          * otherwise
21236          * @static
21237          */
21238         isHandle: function(sDDId, sHandleId) {
21239             return ( this.handleIds[sDDId] &&
21240                             this.handleIds[sDDId][sHandleId] );
21241         },
21242
21243         /**
21244          * Returns the DragDrop instance for a given id
21245          * @method getDDById
21246          * @param {String} id the id of the DragDrop object
21247          * @return {DragDrop} the drag drop object, null if it is not found
21248          * @static
21249          */
21250         getDDById: function(id) {
21251             for (var i in this.ids) {
21252                 if (this.ids[i][id]) {
21253                     return this.ids[i][id];
21254                 }
21255             }
21256             return null;
21257         },
21258
21259         /**
21260          * Fired after a registered DragDrop object gets the mousedown event.
21261          * Sets up the events required to track the object being dragged
21262          * @method handleMouseDown
21263          * @param {Event} e the event
21264          * @param oDD the DragDrop object being dragged
21265          * @private
21266          * @static
21267          */
21268         handleMouseDown: function(e, oDD) {
21269             if(Roo.QuickTips){
21270                 Roo.QuickTips.disable();
21271             }
21272             this.currentTarget = e.getTarget();
21273
21274             this.dragCurrent = oDD;
21275
21276             var el = oDD.getEl();
21277
21278             // track start position
21279             this.startX = e.getPageX();
21280             this.startY = e.getPageY();
21281
21282             this.deltaX = this.startX - el.offsetLeft;
21283             this.deltaY = this.startY - el.offsetTop;
21284
21285             this.dragThreshMet = false;
21286
21287             this.clickTimeout = setTimeout(
21288                     function() {
21289                         var DDM = Roo.dd.DDM;
21290                         DDM.startDrag(DDM.startX, DDM.startY);
21291                     },
21292                     this.clickTimeThresh );
21293         },
21294
21295         /**
21296          * Fired when either the drag pixel threshol or the mousedown hold
21297          * time threshold has been met.
21298          * @method startDrag
21299          * @param x {int} the X position of the original mousedown
21300          * @param y {int} the Y position of the original mousedown
21301          * @static
21302          */
21303         startDrag: function(x, y) {
21304             clearTimeout(this.clickTimeout);
21305             if (this.dragCurrent) {
21306                 this.dragCurrent.b4StartDrag(x, y);
21307                 this.dragCurrent.startDrag(x, y);
21308             }
21309             this.dragThreshMet = true;
21310         },
21311
21312         /**
21313          * Internal function to handle the mouseup event.  Will be invoked
21314          * from the context of the document.
21315          * @method handleMouseUp
21316          * @param {Event} e the event
21317          * @private
21318          * @static
21319          */
21320         handleMouseUp: function(e) {
21321
21322             if(Roo.QuickTips){
21323                 Roo.QuickTips.enable();
21324             }
21325             if (! this.dragCurrent) {
21326                 return;
21327             }
21328
21329             clearTimeout(this.clickTimeout);
21330
21331             if (this.dragThreshMet) {
21332                 this.fireEvents(e, true);
21333             } else {
21334             }
21335
21336             this.stopDrag(e);
21337
21338             this.stopEvent(e);
21339         },
21340
21341         /**
21342          * Utility to stop event propagation and event default, if these
21343          * features are turned on.
21344          * @method stopEvent
21345          * @param {Event} e the event as returned by this.getEvent()
21346          * @static
21347          */
21348         stopEvent: function(e){
21349             if(this.stopPropagation) {
21350                 e.stopPropagation();
21351             }
21352
21353             if (this.preventDefault) {
21354                 e.preventDefault();
21355             }
21356         },
21357
21358         /**
21359          * Internal function to clean up event handlers after the drag
21360          * operation is complete
21361          * @method stopDrag
21362          * @param {Event} e the event
21363          * @private
21364          * @static
21365          */
21366         stopDrag: function(e) {
21367             // Fire the drag end event for the item that was dragged
21368             if (this.dragCurrent) {
21369                 if (this.dragThreshMet) {
21370                     this.dragCurrent.b4EndDrag(e);
21371                     this.dragCurrent.endDrag(e);
21372                 }
21373
21374                 this.dragCurrent.onMouseUp(e);
21375             }
21376
21377             this.dragCurrent = null;
21378             this.dragOvers = {};
21379         },
21380
21381         /**
21382          * Internal function to handle the mousemove event.  Will be invoked
21383          * from the context of the html element.
21384          *
21385          * @TODO figure out what we can do about mouse events lost when the
21386          * user drags objects beyond the window boundary.  Currently we can
21387          * detect this in internet explorer by verifying that the mouse is
21388          * down during the mousemove event.  Firefox doesn't give us the
21389          * button state on the mousemove event.
21390          * @method handleMouseMove
21391          * @param {Event} e the event
21392          * @private
21393          * @static
21394          */
21395         handleMouseMove: function(e) {
21396             if (! this.dragCurrent) {
21397                 return true;
21398             }
21399
21400             // var button = e.which || e.button;
21401
21402             // check for IE mouseup outside of page boundary
21403             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21404                 this.stopEvent(e);
21405                 return this.handleMouseUp(e);
21406             }
21407
21408             if (!this.dragThreshMet) {
21409                 var diffX = Math.abs(this.startX - e.getPageX());
21410                 var diffY = Math.abs(this.startY - e.getPageY());
21411                 if (diffX > this.clickPixelThresh ||
21412                             diffY > this.clickPixelThresh) {
21413                     this.startDrag(this.startX, this.startY);
21414                 }
21415             }
21416
21417             if (this.dragThreshMet) {
21418                 this.dragCurrent.b4Drag(e);
21419                 this.dragCurrent.onDrag(e);
21420                 if(!this.dragCurrent.moveOnly){
21421                     this.fireEvents(e, false);
21422                 }
21423             }
21424
21425             this.stopEvent(e);
21426
21427             return true;
21428         },
21429
21430         /**
21431          * Iterates over all of the DragDrop elements to find ones we are
21432          * hovering over or dropping on
21433          * @method fireEvents
21434          * @param {Event} e the event
21435          * @param {boolean} isDrop is this a drop op or a mouseover op?
21436          * @private
21437          * @static
21438          */
21439         fireEvents: function(e, isDrop) {
21440             var dc = this.dragCurrent;
21441
21442             // If the user did the mouse up outside of the window, we could
21443             // get here even though we have ended the drag.
21444             if (!dc || dc.isLocked()) {
21445                 return;
21446             }
21447
21448             var pt = e.getPoint();
21449
21450             // cache the previous dragOver array
21451             var oldOvers = [];
21452
21453             var outEvts   = [];
21454             var overEvts  = [];
21455             var dropEvts  = [];
21456             var enterEvts = [];
21457
21458             // Check to see if the object(s) we were hovering over is no longer
21459             // being hovered over so we can fire the onDragOut event
21460             for (var i in this.dragOvers) {
21461
21462                 var ddo = this.dragOvers[i];
21463
21464                 if (! this.isTypeOfDD(ddo)) {
21465                     continue;
21466                 }
21467
21468                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21469                     outEvts.push( ddo );
21470                 }
21471
21472                 oldOvers[i] = true;
21473                 delete this.dragOvers[i];
21474             }
21475
21476             for (var sGroup in dc.groups) {
21477
21478                 if ("string" != typeof sGroup) {
21479                     continue;
21480                 }
21481
21482                 for (i in this.ids[sGroup]) {
21483                     var oDD = this.ids[sGroup][i];
21484                     if (! this.isTypeOfDD(oDD)) {
21485                         continue;
21486                     }
21487
21488                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21489                         if (this.isOverTarget(pt, oDD, this.mode)) {
21490                             // look for drop interactions
21491                             if (isDrop) {
21492                                 dropEvts.push( oDD );
21493                             // look for drag enter and drag over interactions
21494                             } else {
21495
21496                                 // initial drag over: dragEnter fires
21497                                 if (!oldOvers[oDD.id]) {
21498                                     enterEvts.push( oDD );
21499                                 // subsequent drag overs: dragOver fires
21500                                 } else {
21501                                     overEvts.push( oDD );
21502                                 }
21503
21504                                 this.dragOvers[oDD.id] = oDD;
21505                             }
21506                         }
21507                     }
21508                 }
21509             }
21510
21511             if (this.mode) {
21512                 if (outEvts.length) {
21513                     dc.b4DragOut(e, outEvts);
21514                     dc.onDragOut(e, outEvts);
21515                 }
21516
21517                 if (enterEvts.length) {
21518                     dc.onDragEnter(e, enterEvts);
21519                 }
21520
21521                 if (overEvts.length) {
21522                     dc.b4DragOver(e, overEvts);
21523                     dc.onDragOver(e, overEvts);
21524                 }
21525
21526                 if (dropEvts.length) {
21527                     dc.b4DragDrop(e, dropEvts);
21528                     dc.onDragDrop(e, dropEvts);
21529                 }
21530
21531             } else {
21532                 // fire dragout events
21533                 var len = 0;
21534                 for (i=0, len=outEvts.length; i<len; ++i) {
21535                     dc.b4DragOut(e, outEvts[i].id);
21536                     dc.onDragOut(e, outEvts[i].id);
21537                 }
21538
21539                 // fire enter events
21540                 for (i=0,len=enterEvts.length; i<len; ++i) {
21541                     // dc.b4DragEnter(e, oDD.id);
21542                     dc.onDragEnter(e, enterEvts[i].id);
21543                 }
21544
21545                 // fire over events
21546                 for (i=0,len=overEvts.length; i<len; ++i) {
21547                     dc.b4DragOver(e, overEvts[i].id);
21548                     dc.onDragOver(e, overEvts[i].id);
21549                 }
21550
21551                 // fire drop events
21552                 for (i=0, len=dropEvts.length; i<len; ++i) {
21553                     dc.b4DragDrop(e, dropEvts[i].id);
21554                     dc.onDragDrop(e, dropEvts[i].id);
21555                 }
21556
21557             }
21558
21559             // notify about a drop that did not find a target
21560             if (isDrop && !dropEvts.length) {
21561                 dc.onInvalidDrop(e);
21562             }
21563
21564         },
21565
21566         /**
21567          * Helper function for getting the best match from the list of drag
21568          * and drop objects returned by the drag and drop events when we are
21569          * in INTERSECT mode.  It returns either the first object that the
21570          * cursor is over, or the object that has the greatest overlap with
21571          * the dragged element.
21572          * @method getBestMatch
21573          * @param  {DragDrop[]} dds The array of drag and drop objects
21574          * targeted
21575          * @return {DragDrop}       The best single match
21576          * @static
21577          */
21578         getBestMatch: function(dds) {
21579             var winner = null;
21580             // Return null if the input is not what we expect
21581             //if (!dds || !dds.length || dds.length == 0) {
21582                // winner = null;
21583             // If there is only one item, it wins
21584             //} else if (dds.length == 1) {
21585
21586             var len = dds.length;
21587
21588             if (len == 1) {
21589                 winner = dds[0];
21590             } else {
21591                 // Loop through the targeted items
21592                 for (var i=0; i<len; ++i) {
21593                     var dd = dds[i];
21594                     // If the cursor is over the object, it wins.  If the
21595                     // cursor is over multiple matches, the first one we come
21596                     // to wins.
21597                     if (dd.cursorIsOver) {
21598                         winner = dd;
21599                         break;
21600                     // Otherwise the object with the most overlap wins
21601                     } else {
21602                         if (!winner ||
21603                             winner.overlap.getArea() < dd.overlap.getArea()) {
21604                             winner = dd;
21605                         }
21606                     }
21607                 }
21608             }
21609
21610             return winner;
21611         },
21612
21613         /**
21614          * Refreshes the cache of the top-left and bottom-right points of the
21615          * drag and drop objects in the specified group(s).  This is in the
21616          * format that is stored in the drag and drop instance, so typical
21617          * usage is:
21618          * <code>
21619          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21620          * </code>
21621          * Alternatively:
21622          * <code>
21623          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21624          * </code>
21625          * @TODO this really should be an indexed array.  Alternatively this
21626          * method could accept both.
21627          * @method refreshCache
21628          * @param {Object} groups an associative array of groups to refresh
21629          * @static
21630          */
21631         refreshCache: function(groups) {
21632             for (var sGroup in groups) {
21633                 if ("string" != typeof sGroup) {
21634                     continue;
21635                 }
21636                 for (var i in this.ids[sGroup]) {
21637                     var oDD = this.ids[sGroup][i];
21638
21639                     if (this.isTypeOfDD(oDD)) {
21640                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21641                         var loc = this.getLocation(oDD);
21642                         if (loc) {
21643                             this.locationCache[oDD.id] = loc;
21644                         } else {
21645                             delete this.locationCache[oDD.id];
21646                             // this will unregister the drag and drop object if
21647                             // the element is not in a usable state
21648                             // oDD.unreg();
21649                         }
21650                     }
21651                 }
21652             }
21653         },
21654
21655         /**
21656          * This checks to make sure an element exists and is in the DOM.  The
21657          * main purpose is to handle cases where innerHTML is used to remove
21658          * drag and drop objects from the DOM.  IE provides an 'unspecified
21659          * error' when trying to access the offsetParent of such an element
21660          * @method verifyEl
21661          * @param {HTMLElement} el the element to check
21662          * @return {boolean} true if the element looks usable
21663          * @static
21664          */
21665         verifyEl: function(el) {
21666             if (el) {
21667                 var parent;
21668                 if(Roo.isIE){
21669                     try{
21670                         parent = el.offsetParent;
21671                     }catch(e){}
21672                 }else{
21673                     parent = el.offsetParent;
21674                 }
21675                 if (parent) {
21676                     return true;
21677                 }
21678             }
21679
21680             return false;
21681         },
21682
21683         /**
21684          * Returns a Region object containing the drag and drop element's position
21685          * and size, including the padding configured for it
21686          * @method getLocation
21687          * @param {DragDrop} oDD the drag and drop object to get the
21688          *                       location for
21689          * @return {Roo.lib.Region} a Region object representing the total area
21690          *                             the element occupies, including any padding
21691          *                             the instance is configured for.
21692          * @static
21693          */
21694         getLocation: function(oDD) {
21695             if (! this.isTypeOfDD(oDD)) {
21696                 return null;
21697             }
21698
21699             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21700
21701             try {
21702                 pos= Roo.lib.Dom.getXY(el);
21703             } catch (e) { }
21704
21705             if (!pos) {
21706                 return null;
21707             }
21708
21709             x1 = pos[0];
21710             x2 = x1 + el.offsetWidth;
21711             y1 = pos[1];
21712             y2 = y1 + el.offsetHeight;
21713
21714             t = y1 - oDD.padding[0];
21715             r = x2 + oDD.padding[1];
21716             b = y2 + oDD.padding[2];
21717             l = x1 - oDD.padding[3];
21718
21719             return new Roo.lib.Region( t, r, b, l );
21720         },
21721
21722         /**
21723          * Checks the cursor location to see if it over the target
21724          * @method isOverTarget
21725          * @param {Roo.lib.Point} pt The point to evaluate
21726          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21727          * @return {boolean} true if the mouse is over the target
21728          * @private
21729          * @static
21730          */
21731         isOverTarget: function(pt, oTarget, intersect) {
21732             // use cache if available
21733             var loc = this.locationCache[oTarget.id];
21734             if (!loc || !this.useCache) {
21735                 loc = this.getLocation(oTarget);
21736                 this.locationCache[oTarget.id] = loc;
21737
21738             }
21739
21740             if (!loc) {
21741                 return false;
21742             }
21743
21744             oTarget.cursorIsOver = loc.contains( pt );
21745
21746             // DragDrop is using this as a sanity check for the initial mousedown
21747             // in this case we are done.  In POINT mode, if the drag obj has no
21748             // contraints, we are also done. Otherwise we need to evaluate the
21749             // location of the target as related to the actual location of the
21750             // dragged element.
21751             var dc = this.dragCurrent;
21752             if (!dc || !dc.getTargetCoord ||
21753                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21754                 return oTarget.cursorIsOver;
21755             }
21756
21757             oTarget.overlap = null;
21758
21759             // Get the current location of the drag element, this is the
21760             // location of the mouse event less the delta that represents
21761             // where the original mousedown happened on the element.  We
21762             // need to consider constraints and ticks as well.
21763             var pos = dc.getTargetCoord(pt.x, pt.y);
21764
21765             var el = dc.getDragEl();
21766             var curRegion = new Roo.lib.Region( pos.y,
21767                                                    pos.x + el.offsetWidth,
21768                                                    pos.y + el.offsetHeight,
21769                                                    pos.x );
21770
21771             var overlap = curRegion.intersect(loc);
21772
21773             if (overlap) {
21774                 oTarget.overlap = overlap;
21775                 return (intersect) ? true : oTarget.cursorIsOver;
21776             } else {
21777                 return false;
21778             }
21779         },
21780
21781         /**
21782          * unload event handler
21783          * @method _onUnload
21784          * @private
21785          * @static
21786          */
21787         _onUnload: function(e, me) {
21788             Roo.dd.DragDropMgr.unregAll();
21789         },
21790
21791         /**
21792          * Cleans up the drag and drop events and objects.
21793          * @method unregAll
21794          * @private
21795          * @static
21796          */
21797         unregAll: function() {
21798
21799             if (this.dragCurrent) {
21800                 this.stopDrag();
21801                 this.dragCurrent = null;
21802             }
21803
21804             this._execOnAll("unreg", []);
21805
21806             for (i in this.elementCache) {
21807                 delete this.elementCache[i];
21808             }
21809
21810             this.elementCache = {};
21811             this.ids = {};
21812         },
21813
21814         /**
21815          * A cache of DOM elements
21816          * @property elementCache
21817          * @private
21818          * @static
21819          */
21820         elementCache: {},
21821
21822         /**
21823          * Get the wrapper for the DOM element specified
21824          * @method getElWrapper
21825          * @param {String} id the id of the element to get
21826          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21827          * @private
21828          * @deprecated This wrapper isn't that useful
21829          * @static
21830          */
21831         getElWrapper: function(id) {
21832             var oWrapper = this.elementCache[id];
21833             if (!oWrapper || !oWrapper.el) {
21834                 oWrapper = this.elementCache[id] =
21835                     new this.ElementWrapper(Roo.getDom(id));
21836             }
21837             return oWrapper;
21838         },
21839
21840         /**
21841          * Returns the actual DOM element
21842          * @method getElement
21843          * @param {String} id the id of the elment to get
21844          * @return {Object} The element
21845          * @deprecated use Roo.getDom instead
21846          * @static
21847          */
21848         getElement: function(id) {
21849             return Roo.getDom(id);
21850         },
21851
21852         /**
21853          * Returns the style property for the DOM element (i.e.,
21854          * document.getElById(id).style)
21855          * @method getCss
21856          * @param {String} id the id of the elment to get
21857          * @return {Object} The style property of the element
21858          * @deprecated use Roo.getDom instead
21859          * @static
21860          */
21861         getCss: function(id) {
21862             var el = Roo.getDom(id);
21863             return (el) ? el.style : null;
21864         },
21865
21866         /**
21867          * Inner class for cached elements
21868          * @class DragDropMgr.ElementWrapper
21869          * @for DragDropMgr
21870          * @private
21871          * @deprecated
21872          */
21873         ElementWrapper: function(el) {
21874                 /**
21875                  * The element
21876                  * @property el
21877                  */
21878                 this.el = el || null;
21879                 /**
21880                  * The element id
21881                  * @property id
21882                  */
21883                 this.id = this.el && el.id;
21884                 /**
21885                  * A reference to the style property
21886                  * @property css
21887                  */
21888                 this.css = this.el && el.style;
21889             },
21890
21891         /**
21892          * Returns the X position of an html element
21893          * @method getPosX
21894          * @param el the element for which to get the position
21895          * @return {int} the X coordinate
21896          * @for DragDropMgr
21897          * @deprecated use Roo.lib.Dom.getX instead
21898          * @static
21899          */
21900         getPosX: function(el) {
21901             return Roo.lib.Dom.getX(el);
21902         },
21903
21904         /**
21905          * Returns the Y position of an html element
21906          * @method getPosY
21907          * @param el the element for which to get the position
21908          * @return {int} the Y coordinate
21909          * @deprecated use Roo.lib.Dom.getY instead
21910          * @static
21911          */
21912         getPosY: function(el) {
21913             return Roo.lib.Dom.getY(el);
21914         },
21915
21916         /**
21917          * Swap two nodes.  In IE, we use the native method, for others we
21918          * emulate the IE behavior
21919          * @method swapNode
21920          * @param n1 the first node to swap
21921          * @param n2 the other node to swap
21922          * @static
21923          */
21924         swapNode: function(n1, n2) {
21925             if (n1.swapNode) {
21926                 n1.swapNode(n2);
21927             } else {
21928                 var p = n2.parentNode;
21929                 var s = n2.nextSibling;
21930
21931                 if (s == n1) {
21932                     p.insertBefore(n1, n2);
21933                 } else if (n2 == n1.nextSibling) {
21934                     p.insertBefore(n2, n1);
21935                 } else {
21936                     n1.parentNode.replaceChild(n2, n1);
21937                     p.insertBefore(n1, s);
21938                 }
21939             }
21940         },
21941
21942         /**
21943          * Returns the current scroll position
21944          * @method getScroll
21945          * @private
21946          * @static
21947          */
21948         getScroll: function () {
21949             var t, l, dde=document.documentElement, db=document.body;
21950             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21951                 t = dde.scrollTop;
21952                 l = dde.scrollLeft;
21953             } else if (db) {
21954                 t = db.scrollTop;
21955                 l = db.scrollLeft;
21956             } else {
21957
21958             }
21959             return { top: t, left: l };
21960         },
21961
21962         /**
21963          * Returns the specified element style property
21964          * @method getStyle
21965          * @param {HTMLElement} el          the element
21966          * @param {string}      styleProp   the style property
21967          * @return {string} The value of the style property
21968          * @deprecated use Roo.lib.Dom.getStyle
21969          * @static
21970          */
21971         getStyle: function(el, styleProp) {
21972             return Roo.fly(el).getStyle(styleProp);
21973         },
21974
21975         /**
21976          * Gets the scrollTop
21977          * @method getScrollTop
21978          * @return {int} the document's scrollTop
21979          * @static
21980          */
21981         getScrollTop: function () { return this.getScroll().top; },
21982
21983         /**
21984          * Gets the scrollLeft
21985          * @method getScrollLeft
21986          * @return {int} the document's scrollTop
21987          * @static
21988          */
21989         getScrollLeft: function () { return this.getScroll().left; },
21990
21991         /**
21992          * Sets the x/y position of an element to the location of the
21993          * target element.
21994          * @method moveToEl
21995          * @param {HTMLElement} moveEl      The element to move
21996          * @param {HTMLElement} targetEl    The position reference element
21997          * @static
21998          */
21999         moveToEl: function (moveEl, targetEl) {
22000             var aCoord = Roo.lib.Dom.getXY(targetEl);
22001             Roo.lib.Dom.setXY(moveEl, aCoord);
22002         },
22003
22004         /**
22005          * Numeric array sort function
22006          * @method numericSort
22007          * @static
22008          */
22009         numericSort: function(a, b) { return (a - b); },
22010
22011         /**
22012          * Internal counter
22013          * @property _timeoutCount
22014          * @private
22015          * @static
22016          */
22017         _timeoutCount: 0,
22018
22019         /**
22020          * Trying to make the load order less important.  Without this we get
22021          * an error if this file is loaded before the Event Utility.
22022          * @method _addListeners
22023          * @private
22024          * @static
22025          */
22026         _addListeners: function() {
22027             var DDM = Roo.dd.DDM;
22028             if ( Roo.lib.Event && document ) {
22029                 DDM._onLoad();
22030             } else {
22031                 if (DDM._timeoutCount > 2000) {
22032                 } else {
22033                     setTimeout(DDM._addListeners, 10);
22034                     if (document && document.body) {
22035                         DDM._timeoutCount += 1;
22036                     }
22037                 }
22038             }
22039         },
22040
22041         /**
22042          * Recursively searches the immediate parent and all child nodes for
22043          * the handle element in order to determine wheter or not it was
22044          * clicked.
22045          * @method handleWasClicked
22046          * @param node the html element to inspect
22047          * @static
22048          */
22049         handleWasClicked: function(node, id) {
22050             if (this.isHandle(id, node.id)) {
22051                 return true;
22052             } else {
22053                 // check to see if this is a text node child of the one we want
22054                 var p = node.parentNode;
22055
22056                 while (p) {
22057                     if (this.isHandle(id, p.id)) {
22058                         return true;
22059                     } else {
22060                         p = p.parentNode;
22061                     }
22062                 }
22063             }
22064
22065             return false;
22066         }
22067
22068     };
22069
22070 }();
22071
22072 // shorter alias, save a few bytes
22073 Roo.dd.DDM = Roo.dd.DragDropMgr;
22074 Roo.dd.DDM._addListeners();
22075
22076 }/*
22077  * Based on:
22078  * Ext JS Library 1.1.1
22079  * Copyright(c) 2006-2007, Ext JS, LLC.
22080  *
22081  * Originally Released Under LGPL - original licence link has changed is not relivant.
22082  *
22083  * Fork - LGPL
22084  * <script type="text/javascript">
22085  */
22086
22087 /**
22088  * @class Roo.dd.DD
22089  * A DragDrop implementation where the linked element follows the
22090  * mouse cursor during a drag.
22091  * @extends Roo.dd.DragDrop
22092  * @constructor
22093  * @param {String} id the id of the linked element
22094  * @param {String} sGroup the group of related DragDrop items
22095  * @param {object} config an object containing configurable attributes
22096  *                Valid properties for DD:
22097  *                    scroll
22098  */
22099 Roo.dd.DD = function(id, sGroup, config) {
22100     if (id) {
22101         this.init(id, sGroup, config);
22102     }
22103 };
22104
22105 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22106
22107     /**
22108      * When set to true, the utility automatically tries to scroll the browser
22109      * window wehn a drag and drop element is dragged near the viewport boundary.
22110      * Defaults to true.
22111      * @property scroll
22112      * @type boolean
22113      */
22114     scroll: true,
22115
22116     /**
22117      * Sets the pointer offset to the distance between the linked element's top
22118      * left corner and the location the element was clicked
22119      * @method autoOffset
22120      * @param {int} iPageX the X coordinate of the click
22121      * @param {int} iPageY the Y coordinate of the click
22122      */
22123     autoOffset: function(iPageX, iPageY) {
22124         var x = iPageX - this.startPageX;
22125         var y = iPageY - this.startPageY;
22126         this.setDelta(x, y);
22127     },
22128
22129     /**
22130      * Sets the pointer offset.  You can call this directly to force the
22131      * offset to be in a particular location (e.g., pass in 0,0 to set it
22132      * to the center of the object)
22133      * @method setDelta
22134      * @param {int} iDeltaX the distance from the left
22135      * @param {int} iDeltaY the distance from the top
22136      */
22137     setDelta: function(iDeltaX, iDeltaY) {
22138         this.deltaX = iDeltaX;
22139         this.deltaY = iDeltaY;
22140     },
22141
22142     /**
22143      * Sets the drag element to the location of the mousedown or click event,
22144      * maintaining the cursor location relative to the location on the element
22145      * that was clicked.  Override this if you want to place the element in a
22146      * location other than where the cursor is.
22147      * @method setDragElPos
22148      * @param {int} iPageX the X coordinate of the mousedown or drag event
22149      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22150      */
22151     setDragElPos: function(iPageX, iPageY) {
22152         // the first time we do this, we are going to check to make sure
22153         // the element has css positioning
22154
22155         var el = this.getDragEl();
22156         this.alignElWithMouse(el, iPageX, iPageY);
22157     },
22158
22159     /**
22160      * Sets the element to the location of the mousedown or click event,
22161      * maintaining the cursor location relative to the location on the element
22162      * that was clicked.  Override this if you want to place the element in a
22163      * location other than where the cursor is.
22164      * @method alignElWithMouse
22165      * @param {HTMLElement} el the element to move
22166      * @param {int} iPageX the X coordinate of the mousedown or drag event
22167      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22168      */
22169     alignElWithMouse: function(el, iPageX, iPageY) {
22170         var oCoord = this.getTargetCoord(iPageX, iPageY);
22171         var fly = el.dom ? el : Roo.fly(el);
22172         if (!this.deltaSetXY) {
22173             var aCoord = [oCoord.x, oCoord.y];
22174             fly.setXY(aCoord);
22175             var newLeft = fly.getLeft(true);
22176             var newTop  = fly.getTop(true);
22177             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22178         } else {
22179             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22180         }
22181
22182         this.cachePosition(oCoord.x, oCoord.y);
22183         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22184         return oCoord;
22185     },
22186
22187     /**
22188      * Saves the most recent position so that we can reset the constraints and
22189      * tick marks on-demand.  We need to know this so that we can calculate the
22190      * number of pixels the element is offset from its original position.
22191      * @method cachePosition
22192      * @param iPageX the current x position (optional, this just makes it so we
22193      * don't have to look it up again)
22194      * @param iPageY the current y position (optional, this just makes it so we
22195      * don't have to look it up again)
22196      */
22197     cachePosition: function(iPageX, iPageY) {
22198         if (iPageX) {
22199             this.lastPageX = iPageX;
22200             this.lastPageY = iPageY;
22201         } else {
22202             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22203             this.lastPageX = aCoord[0];
22204             this.lastPageY = aCoord[1];
22205         }
22206     },
22207
22208     /**
22209      * Auto-scroll the window if the dragged object has been moved beyond the
22210      * visible window boundary.
22211      * @method autoScroll
22212      * @param {int} x the drag element's x position
22213      * @param {int} y the drag element's y position
22214      * @param {int} h the height of the drag element
22215      * @param {int} w the width of the drag element
22216      * @private
22217      */
22218     autoScroll: function(x, y, h, w) {
22219
22220         if (this.scroll) {
22221             // The client height
22222             var clientH = Roo.lib.Dom.getViewWidth();
22223
22224             // The client width
22225             var clientW = Roo.lib.Dom.getViewHeight();
22226
22227             // The amt scrolled down
22228             var st = this.DDM.getScrollTop();
22229
22230             // The amt scrolled right
22231             var sl = this.DDM.getScrollLeft();
22232
22233             // Location of the bottom of the element
22234             var bot = h + y;
22235
22236             // Location of the right of the element
22237             var right = w + x;
22238
22239             // The distance from the cursor to the bottom of the visible area,
22240             // adjusted so that we don't scroll if the cursor is beyond the
22241             // element drag constraints
22242             var toBot = (clientH + st - y - this.deltaY);
22243
22244             // The distance from the cursor to the right of the visible area
22245             var toRight = (clientW + sl - x - this.deltaX);
22246
22247
22248             // How close to the edge the cursor must be before we scroll
22249             // var thresh = (document.all) ? 100 : 40;
22250             var thresh = 40;
22251
22252             // How many pixels to scroll per autoscroll op.  This helps to reduce
22253             // clunky scrolling. IE is more sensitive about this ... it needs this
22254             // value to be higher.
22255             var scrAmt = (document.all) ? 80 : 30;
22256
22257             // Scroll down if we are near the bottom of the visible page and the
22258             // obj extends below the crease
22259             if ( bot > clientH && toBot < thresh ) {
22260                 window.scrollTo(sl, st + scrAmt);
22261             }
22262
22263             // Scroll up if the window is scrolled down and the top of the object
22264             // goes above the top border
22265             if ( y < st && st > 0 && y - st < thresh ) {
22266                 window.scrollTo(sl, st - scrAmt);
22267             }
22268
22269             // Scroll right if the obj is beyond the right border and the cursor is
22270             // near the border.
22271             if ( right > clientW && toRight < thresh ) {
22272                 window.scrollTo(sl + scrAmt, st);
22273             }
22274
22275             // Scroll left if the window has been scrolled to the right and the obj
22276             // extends past the left border
22277             if ( x < sl && sl > 0 && x - sl < thresh ) {
22278                 window.scrollTo(sl - scrAmt, st);
22279             }
22280         }
22281     },
22282
22283     /**
22284      * Finds the location the element should be placed if we want to move
22285      * it to where the mouse location less the click offset would place us.
22286      * @method getTargetCoord
22287      * @param {int} iPageX the X coordinate of the click
22288      * @param {int} iPageY the Y coordinate of the click
22289      * @return an object that contains the coordinates (Object.x and Object.y)
22290      * @private
22291      */
22292     getTargetCoord: function(iPageX, iPageY) {
22293
22294
22295         var x = iPageX - this.deltaX;
22296         var y = iPageY - this.deltaY;
22297
22298         if (this.constrainX) {
22299             if (x < this.minX) { x = this.minX; }
22300             if (x > this.maxX) { x = this.maxX; }
22301         }
22302
22303         if (this.constrainY) {
22304             if (y < this.minY) { y = this.minY; }
22305             if (y > this.maxY) { y = this.maxY; }
22306         }
22307
22308         x = this.getTick(x, this.xTicks);
22309         y = this.getTick(y, this.yTicks);
22310
22311
22312         return {x:x, y:y};
22313     },
22314
22315     /*
22316      * Sets up config options specific to this class. Overrides
22317      * Roo.dd.DragDrop, but all versions of this method through the
22318      * inheritance chain are called
22319      */
22320     applyConfig: function() {
22321         Roo.dd.DD.superclass.applyConfig.call(this);
22322         this.scroll = (this.config.scroll !== false);
22323     },
22324
22325     /*
22326      * Event that fires prior to the onMouseDown event.  Overrides
22327      * Roo.dd.DragDrop.
22328      */
22329     b4MouseDown: function(e) {
22330         // this.resetConstraints();
22331         this.autoOffset(e.getPageX(),
22332                             e.getPageY());
22333     },
22334
22335     /*
22336      * Event that fires prior to the onDrag event.  Overrides
22337      * Roo.dd.DragDrop.
22338      */
22339     b4Drag: function(e) {
22340         this.setDragElPos(e.getPageX(),
22341                             e.getPageY());
22342     },
22343
22344     toString: function() {
22345         return ("DD " + this.id);
22346     }
22347
22348     //////////////////////////////////////////////////////////////////////////
22349     // Debugging ygDragDrop events that can be overridden
22350     //////////////////////////////////////////////////////////////////////////
22351     /*
22352     startDrag: function(x, y) {
22353     },
22354
22355     onDrag: function(e) {
22356     },
22357
22358     onDragEnter: function(e, id) {
22359     },
22360
22361     onDragOver: function(e, id) {
22362     },
22363
22364     onDragOut: function(e, id) {
22365     },
22366
22367     onDragDrop: function(e, id) {
22368     },
22369
22370     endDrag: function(e) {
22371     }
22372
22373     */
22374
22375 });/*
22376  * Based on:
22377  * Ext JS Library 1.1.1
22378  * Copyright(c) 2006-2007, Ext JS, LLC.
22379  *
22380  * Originally Released Under LGPL - original licence link has changed is not relivant.
22381  *
22382  * Fork - LGPL
22383  * <script type="text/javascript">
22384  */
22385
22386 /**
22387  * @class Roo.dd.DDProxy
22388  * A DragDrop implementation that inserts an empty, bordered div into
22389  * the document that follows the cursor during drag operations.  At the time of
22390  * the click, the frame div is resized to the dimensions of the linked html
22391  * element, and moved to the exact location of the linked element.
22392  *
22393  * References to the "frame" element refer to the single proxy element that
22394  * was created to be dragged in place of all DDProxy elements on the
22395  * page.
22396  *
22397  * @extends Roo.dd.DD
22398  * @constructor
22399  * @param {String} id the id of the linked html element
22400  * @param {String} sGroup the group of related DragDrop objects
22401  * @param {object} config an object containing configurable attributes
22402  *                Valid properties for DDProxy in addition to those in DragDrop:
22403  *                   resizeFrame, centerFrame, dragElId
22404  */
22405 Roo.dd.DDProxy = function(id, sGroup, config) {
22406     if (id) {
22407         this.init(id, sGroup, config);
22408         this.initFrame();
22409     }
22410 };
22411
22412 /**
22413  * The default drag frame div id
22414  * @property Roo.dd.DDProxy.dragElId
22415  * @type String
22416  * @static
22417  */
22418 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22419
22420 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22421
22422     /**
22423      * By default we resize the drag frame to be the same size as the element
22424      * we want to drag (this is to get the frame effect).  We can turn it off
22425      * if we want a different behavior.
22426      * @property resizeFrame
22427      * @type boolean
22428      */
22429     resizeFrame: true,
22430
22431     /**
22432      * By default the frame is positioned exactly where the drag element is, so
22433      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22434      * you do not have constraints on the obj is to have the drag frame centered
22435      * around the cursor.  Set centerFrame to true for this effect.
22436      * @property centerFrame
22437      * @type boolean
22438      */
22439     centerFrame: false,
22440
22441     /**
22442      * Creates the proxy element if it does not yet exist
22443      * @method createFrame
22444      */
22445     createFrame: function() {
22446         var self = this;
22447         var body = document.body;
22448
22449         if (!body || !body.firstChild) {
22450             setTimeout( function() { self.createFrame(); }, 50 );
22451             return;
22452         }
22453
22454         var div = this.getDragEl();
22455
22456         if (!div) {
22457             div    = document.createElement("div");
22458             div.id = this.dragElId;
22459             var s  = div.style;
22460
22461             s.position   = "absolute";
22462             s.visibility = "hidden";
22463             s.cursor     = "move";
22464             s.border     = "2px solid #aaa";
22465             s.zIndex     = 999;
22466
22467             // appendChild can blow up IE if invoked prior to the window load event
22468             // while rendering a table.  It is possible there are other scenarios
22469             // that would cause this to happen as well.
22470             body.insertBefore(div, body.firstChild);
22471         }
22472     },
22473
22474     /**
22475      * Initialization for the drag frame element.  Must be called in the
22476      * constructor of all subclasses
22477      * @method initFrame
22478      */
22479     initFrame: function() {
22480         this.createFrame();
22481     },
22482
22483     applyConfig: function() {
22484         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22485
22486         this.resizeFrame = (this.config.resizeFrame !== false);
22487         this.centerFrame = (this.config.centerFrame);
22488         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22489     },
22490
22491     /**
22492      * Resizes the drag frame to the dimensions of the clicked object, positions
22493      * it over the object, and finally displays it
22494      * @method showFrame
22495      * @param {int} iPageX X click position
22496      * @param {int} iPageY Y click position
22497      * @private
22498      */
22499     showFrame: function(iPageX, iPageY) {
22500         var el = this.getEl();
22501         var dragEl = this.getDragEl();
22502         var s = dragEl.style;
22503
22504         this._resizeProxy();
22505
22506         if (this.centerFrame) {
22507             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22508                            Math.round(parseInt(s.height, 10)/2) );
22509         }
22510
22511         this.setDragElPos(iPageX, iPageY);
22512
22513         Roo.fly(dragEl).show();
22514     },
22515
22516     /**
22517      * The proxy is automatically resized to the dimensions of the linked
22518      * element when a drag is initiated, unless resizeFrame is set to false
22519      * @method _resizeProxy
22520      * @private
22521      */
22522     _resizeProxy: function() {
22523         if (this.resizeFrame) {
22524             var el = this.getEl();
22525             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22526         }
22527     },
22528
22529     // overrides Roo.dd.DragDrop
22530     b4MouseDown: function(e) {
22531         var x = e.getPageX();
22532         var y = e.getPageY();
22533         this.autoOffset(x, y);
22534         this.setDragElPos(x, y);
22535     },
22536
22537     // overrides Roo.dd.DragDrop
22538     b4StartDrag: function(x, y) {
22539         // show the drag frame
22540         this.showFrame(x, y);
22541     },
22542
22543     // overrides Roo.dd.DragDrop
22544     b4EndDrag: function(e) {
22545         Roo.fly(this.getDragEl()).hide();
22546     },
22547
22548     // overrides Roo.dd.DragDrop
22549     // By default we try to move the element to the last location of the frame.
22550     // This is so that the default behavior mirrors that of Roo.dd.DD.
22551     endDrag: function(e) {
22552
22553         var lel = this.getEl();
22554         var del = this.getDragEl();
22555
22556         // Show the drag frame briefly so we can get its position
22557         del.style.visibility = "";
22558
22559         this.beforeMove();
22560         // Hide the linked element before the move to get around a Safari
22561         // rendering bug.
22562         lel.style.visibility = "hidden";
22563         Roo.dd.DDM.moveToEl(lel, del);
22564         del.style.visibility = "hidden";
22565         lel.style.visibility = "";
22566
22567         this.afterDrag();
22568     },
22569
22570     beforeMove : function(){
22571
22572     },
22573
22574     afterDrag : function(){
22575
22576     },
22577
22578     toString: function() {
22579         return ("DDProxy " + this.id);
22580     }
22581
22582 });
22583 /*
22584  * Based on:
22585  * Ext JS Library 1.1.1
22586  * Copyright(c) 2006-2007, Ext JS, LLC.
22587  *
22588  * Originally Released Under LGPL - original licence link has changed is not relivant.
22589  *
22590  * Fork - LGPL
22591  * <script type="text/javascript">
22592  */
22593
22594  /**
22595  * @class Roo.dd.DDTarget
22596  * A DragDrop implementation that does not move, but can be a drop
22597  * target.  You would get the same result by simply omitting implementation
22598  * for the event callbacks, but this way we reduce the processing cost of the
22599  * event listener and the callbacks.
22600  * @extends Roo.dd.DragDrop
22601  * @constructor
22602  * @param {String} id the id of the element that is a drop target
22603  * @param {String} sGroup the group of related DragDrop objects
22604  * @param {object} config an object containing configurable attributes
22605  *                 Valid properties for DDTarget in addition to those in
22606  *                 DragDrop:
22607  *                    none
22608  */
22609 Roo.dd.DDTarget = function(id, sGroup, config) {
22610     if (id) {
22611         this.initTarget(id, sGroup, config);
22612     }
22613     if (config && (config.listeners || config.events)) { 
22614         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22615             listeners : config.listeners || {}, 
22616             events : config.events || {} 
22617         });    
22618     }
22619 };
22620
22621 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22622 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22623     toString: function() {
22624         return ("DDTarget " + this.id);
22625     }
22626 });
22627 /*
22628  * Based on:
22629  * Ext JS Library 1.1.1
22630  * Copyright(c) 2006-2007, Ext JS, LLC.
22631  *
22632  * Originally Released Under LGPL - original licence link has changed is not relivant.
22633  *
22634  * Fork - LGPL
22635  * <script type="text/javascript">
22636  */
22637  
22638
22639 /**
22640  * @class Roo.dd.ScrollManager
22641  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22642  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22643  * @static
22644  */
22645 Roo.dd.ScrollManager = function(){
22646     var ddm = Roo.dd.DragDropMgr;
22647     var els = {};
22648     var dragEl = null;
22649     var proc = {};
22650     
22651     
22652     
22653     var onStop = function(e){
22654         dragEl = null;
22655         clearProc();
22656     };
22657     
22658     var triggerRefresh = function(){
22659         if(ddm.dragCurrent){
22660              ddm.refreshCache(ddm.dragCurrent.groups);
22661         }
22662     };
22663     
22664     var doScroll = function(){
22665         if(ddm.dragCurrent){
22666             var dds = Roo.dd.ScrollManager;
22667             if(!dds.animate){
22668                 if(proc.el.scroll(proc.dir, dds.increment)){
22669                     triggerRefresh();
22670                 }
22671             }else{
22672                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22673             }
22674         }
22675     };
22676     
22677     var clearProc = function(){
22678         if(proc.id){
22679             clearInterval(proc.id);
22680         }
22681         proc.id = 0;
22682         proc.el = null;
22683         proc.dir = "";
22684     };
22685     
22686     var startProc = function(el, dir){
22687          Roo.log('scroll startproc');
22688         clearProc();
22689         proc.el = el;
22690         proc.dir = dir;
22691         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22692     };
22693     
22694     var onFire = function(e, isDrop){
22695        
22696         if(isDrop || !ddm.dragCurrent){ return; }
22697         var dds = Roo.dd.ScrollManager;
22698         if(!dragEl || dragEl != ddm.dragCurrent){
22699             dragEl = ddm.dragCurrent;
22700             // refresh regions on drag start
22701             dds.refreshCache();
22702         }
22703         
22704         var xy = Roo.lib.Event.getXY(e);
22705         var pt = new Roo.lib.Point(xy[0], xy[1]);
22706         for(var id in els){
22707             var el = els[id], r = el._region;
22708             if(r && r.contains(pt) && el.isScrollable()){
22709                 if(r.bottom - pt.y <= dds.thresh){
22710                     if(proc.el != el){
22711                         startProc(el, "down");
22712                     }
22713                     return;
22714                 }else if(r.right - pt.x <= dds.thresh){
22715                     if(proc.el != el){
22716                         startProc(el, "left");
22717                     }
22718                     return;
22719                 }else if(pt.y - r.top <= dds.thresh){
22720                     if(proc.el != el){
22721                         startProc(el, "up");
22722                     }
22723                     return;
22724                 }else if(pt.x - r.left <= dds.thresh){
22725                     if(proc.el != el){
22726                         startProc(el, "right");
22727                     }
22728                     return;
22729                 }
22730             }
22731         }
22732         clearProc();
22733     };
22734     
22735     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22736     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22737     
22738     return {
22739         /**
22740          * Registers new overflow element(s) to auto scroll
22741          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22742          */
22743         register : function(el){
22744             if(el instanceof Array){
22745                 for(var i = 0, len = el.length; i < len; i++) {
22746                         this.register(el[i]);
22747                 }
22748             }else{
22749                 el = Roo.get(el);
22750                 els[el.id] = el;
22751             }
22752             Roo.dd.ScrollManager.els = els;
22753         },
22754         
22755         /**
22756          * Unregisters overflow element(s) so they are no longer scrolled
22757          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22758          */
22759         unregister : function(el){
22760             if(el instanceof Array){
22761                 for(var i = 0, len = el.length; i < len; i++) {
22762                         this.unregister(el[i]);
22763                 }
22764             }else{
22765                 el = Roo.get(el);
22766                 delete els[el.id];
22767             }
22768         },
22769         
22770         /**
22771          * The number of pixels from the edge of a container the pointer needs to be to 
22772          * trigger scrolling (defaults to 25)
22773          * @type Number
22774          */
22775         thresh : 25,
22776         
22777         /**
22778          * The number of pixels to scroll in each scroll increment (defaults to 50)
22779          * @type Number
22780          */
22781         increment : 100,
22782         
22783         /**
22784          * The frequency of scrolls in milliseconds (defaults to 500)
22785          * @type Number
22786          */
22787         frequency : 500,
22788         
22789         /**
22790          * True to animate the scroll (defaults to true)
22791          * @type Boolean
22792          */
22793         animate: true,
22794         
22795         /**
22796          * The animation duration in seconds - 
22797          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22798          * @type Number
22799          */
22800         animDuration: .4,
22801         
22802         /**
22803          * Manually trigger a cache refresh.
22804          */
22805         refreshCache : function(){
22806             for(var id in els){
22807                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22808                     els[id]._region = els[id].getRegion();
22809                 }
22810             }
22811         }
22812     };
22813 }();/*
22814  * Based on:
22815  * Ext JS Library 1.1.1
22816  * Copyright(c) 2006-2007, Ext JS, LLC.
22817  *
22818  * Originally Released Under LGPL - original licence link has changed is not relivant.
22819  *
22820  * Fork - LGPL
22821  * <script type="text/javascript">
22822  */
22823  
22824
22825 /**
22826  * @class Roo.dd.Registry
22827  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22828  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22829  * @static
22830  */
22831 Roo.dd.Registry = function(){
22832     var elements = {}; 
22833     var handles = {}; 
22834     var autoIdSeed = 0;
22835
22836     var getId = function(el, autogen){
22837         if(typeof el == "string"){
22838             return el;
22839         }
22840         var id = el.id;
22841         if(!id && autogen !== false){
22842             id = "roodd-" + (++autoIdSeed);
22843             el.id = id;
22844         }
22845         return id;
22846     };
22847     
22848     return {
22849     /**
22850      * Register a drag drop element
22851      * @param {String|HTMLElement} element The id or DOM node to register
22852      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22853      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22854      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22855      * populated in the data object (if applicable):
22856      * <pre>
22857 Value      Description<br />
22858 ---------  ------------------------------------------<br />
22859 handles    Array of DOM nodes that trigger dragging<br />
22860            for the element being registered<br />
22861 isHandle   True if the element passed in triggers<br />
22862            dragging itself, else false
22863 </pre>
22864      */
22865         register : function(el, data){
22866             data = data || {};
22867             if(typeof el == "string"){
22868                 el = document.getElementById(el);
22869             }
22870             data.ddel = el;
22871             elements[getId(el)] = data;
22872             if(data.isHandle !== false){
22873                 handles[data.ddel.id] = data;
22874             }
22875             if(data.handles){
22876                 var hs = data.handles;
22877                 for(var i = 0, len = hs.length; i < len; i++){
22878                         handles[getId(hs[i])] = data;
22879                 }
22880             }
22881         },
22882
22883     /**
22884      * Unregister a drag drop element
22885      * @param {String|HTMLElement}  element The id or DOM node to unregister
22886      */
22887         unregister : function(el){
22888             var id = getId(el, false);
22889             var data = elements[id];
22890             if(data){
22891                 delete elements[id];
22892                 if(data.handles){
22893                     var hs = data.handles;
22894                     for(var i = 0, len = hs.length; i < len; i++){
22895                         delete handles[getId(hs[i], false)];
22896                     }
22897                 }
22898             }
22899         },
22900
22901     /**
22902      * Returns the handle registered for a DOM Node by id
22903      * @param {String|HTMLElement} id The DOM node or id to look up
22904      * @return {Object} handle The custom handle data
22905      */
22906         getHandle : function(id){
22907             if(typeof id != "string"){ // must be element?
22908                 id = id.id;
22909             }
22910             return handles[id];
22911         },
22912
22913     /**
22914      * Returns the handle that is registered for the DOM node that is the target of the event
22915      * @param {Event} e The event
22916      * @return {Object} handle The custom handle data
22917      */
22918         getHandleFromEvent : function(e){
22919             var t = Roo.lib.Event.getTarget(e);
22920             return t ? handles[t.id] : null;
22921         },
22922
22923     /**
22924      * Returns a custom data object that is registered for a DOM node by id
22925      * @param {String|HTMLElement} id The DOM node or id to look up
22926      * @return {Object} data The custom data
22927      */
22928         getTarget : function(id){
22929             if(typeof id != "string"){ // must be element?
22930                 id = id.id;
22931             }
22932             return elements[id];
22933         },
22934
22935     /**
22936      * Returns a custom data object that is registered for the DOM node that is the target of the event
22937      * @param {Event} e The event
22938      * @return {Object} data The custom data
22939      */
22940         getTargetFromEvent : function(e){
22941             var t = Roo.lib.Event.getTarget(e);
22942             return t ? elements[t.id] || handles[t.id] : null;
22943         }
22944     };
22945 }();/*
22946  * Based on:
22947  * Ext JS Library 1.1.1
22948  * Copyright(c) 2006-2007, Ext JS, LLC.
22949  *
22950  * Originally Released Under LGPL - original licence link has changed is not relivant.
22951  *
22952  * Fork - LGPL
22953  * <script type="text/javascript">
22954  */
22955  
22956
22957 /**
22958  * @class Roo.dd.StatusProxy
22959  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22960  * default drag proxy used by all Roo.dd components.
22961  * @constructor
22962  * @param {Object} config
22963  */
22964 Roo.dd.StatusProxy = function(config){
22965     Roo.apply(this, config);
22966     this.id = this.id || Roo.id();
22967     this.el = new Roo.Layer({
22968         dh: {
22969             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22970                 {tag: "div", cls: "x-dd-drop-icon"},
22971                 {tag: "div", cls: "x-dd-drag-ghost"}
22972             ]
22973         }, 
22974         shadow: !config || config.shadow !== false
22975     });
22976     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22977     this.dropStatus = this.dropNotAllowed;
22978 };
22979
22980 Roo.dd.StatusProxy.prototype = {
22981     /**
22982      * @cfg {String} dropAllowed
22983      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22984      */
22985     dropAllowed : "x-dd-drop-ok",
22986     /**
22987      * @cfg {String} dropNotAllowed
22988      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22989      */
22990     dropNotAllowed : "x-dd-drop-nodrop",
22991
22992     /**
22993      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22994      * over the current target element.
22995      * @param {String} cssClass The css class for the new drop status indicator image
22996      */
22997     setStatus : function(cssClass){
22998         cssClass = cssClass || this.dropNotAllowed;
22999         if(this.dropStatus != cssClass){
23000             this.el.replaceClass(this.dropStatus, cssClass);
23001             this.dropStatus = cssClass;
23002         }
23003     },
23004
23005     /**
23006      * Resets the status indicator to the default dropNotAllowed value
23007      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23008      */
23009     reset : function(clearGhost){
23010         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23011         this.dropStatus = this.dropNotAllowed;
23012         if(clearGhost){
23013             this.ghost.update("");
23014         }
23015     },
23016
23017     /**
23018      * Updates the contents of the ghost element
23019      * @param {String} html The html that will replace the current innerHTML of the ghost element
23020      */
23021     update : function(html){
23022         if(typeof html == "string"){
23023             this.ghost.update(html);
23024         }else{
23025             this.ghost.update("");
23026             html.style.margin = "0";
23027             this.ghost.dom.appendChild(html);
23028         }
23029         // ensure float = none set?? cant remember why though.
23030         var el = this.ghost.dom.firstChild;
23031                 if(el){
23032                         Roo.fly(el).setStyle('float', 'none');
23033                 }
23034     },
23035     
23036     /**
23037      * Returns the underlying proxy {@link Roo.Layer}
23038      * @return {Roo.Layer} el
23039     */
23040     getEl : function(){
23041         return this.el;
23042     },
23043
23044     /**
23045      * Returns the ghost element
23046      * @return {Roo.Element} el
23047      */
23048     getGhost : function(){
23049         return this.ghost;
23050     },
23051
23052     /**
23053      * Hides the proxy
23054      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23055      */
23056     hide : function(clear){
23057         this.el.hide();
23058         if(clear){
23059             this.reset(true);
23060         }
23061     },
23062
23063     /**
23064      * Stops the repair animation if it's currently running
23065      */
23066     stop : function(){
23067         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23068             this.anim.stop();
23069         }
23070     },
23071
23072     /**
23073      * Displays this proxy
23074      */
23075     show : function(){
23076         this.el.show();
23077     },
23078
23079     /**
23080      * Force the Layer to sync its shadow and shim positions to the element
23081      */
23082     sync : function(){
23083         this.el.sync();
23084     },
23085
23086     /**
23087      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23088      * invalid drop operation by the item being dragged.
23089      * @param {Array} xy The XY position of the element ([x, y])
23090      * @param {Function} callback The function to call after the repair is complete
23091      * @param {Object} scope The scope in which to execute the callback
23092      */
23093     repair : function(xy, callback, scope){
23094         this.callback = callback;
23095         this.scope = scope;
23096         if(xy && this.animRepair !== false){
23097             this.el.addClass("x-dd-drag-repair");
23098             this.el.hideUnders(true);
23099             this.anim = this.el.shift({
23100                 duration: this.repairDuration || .5,
23101                 easing: 'easeOut',
23102                 xy: xy,
23103                 stopFx: true,
23104                 callback: this.afterRepair,
23105                 scope: this
23106             });
23107         }else{
23108             this.afterRepair();
23109         }
23110     },
23111
23112     // private
23113     afterRepair : function(){
23114         this.hide(true);
23115         if(typeof this.callback == "function"){
23116             this.callback.call(this.scope || this);
23117         }
23118         this.callback = null;
23119         this.scope = null;
23120     }
23121 };/*
23122  * Based on:
23123  * Ext JS Library 1.1.1
23124  * Copyright(c) 2006-2007, Ext JS, LLC.
23125  *
23126  * Originally Released Under LGPL - original licence link has changed is not relivant.
23127  *
23128  * Fork - LGPL
23129  * <script type="text/javascript">
23130  */
23131
23132 /**
23133  * @class Roo.dd.DragSource
23134  * @extends Roo.dd.DDProxy
23135  * A simple class that provides the basic implementation needed to make any element draggable.
23136  * @constructor
23137  * @param {String/HTMLElement/Element} el The container element
23138  * @param {Object} config
23139  */
23140 Roo.dd.DragSource = function(el, config){
23141     this.el = Roo.get(el);
23142     this.dragData = {};
23143     
23144     Roo.apply(this, config);
23145     
23146     if(!this.proxy){
23147         this.proxy = new Roo.dd.StatusProxy();
23148     }
23149
23150     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23151           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23152     
23153     this.dragging = false;
23154 };
23155
23156 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23157     /**
23158      * @cfg {String} dropAllowed
23159      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23160      */
23161     dropAllowed : "x-dd-drop-ok",
23162     /**
23163      * @cfg {String} dropNotAllowed
23164      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23165      */
23166     dropNotAllowed : "x-dd-drop-nodrop",
23167
23168     /**
23169      * Returns the data object associated with this drag source
23170      * @return {Object} data An object containing arbitrary data
23171      */
23172     getDragData : function(e){
23173         return this.dragData;
23174     },
23175
23176     // private
23177     onDragEnter : function(e, id){
23178         var target = Roo.dd.DragDropMgr.getDDById(id);
23179         this.cachedTarget = target;
23180         if(this.beforeDragEnter(target, e, id) !== false){
23181             if(target.isNotifyTarget){
23182                 var status = target.notifyEnter(this, e, this.dragData);
23183                 this.proxy.setStatus(status);
23184             }else{
23185                 this.proxy.setStatus(this.dropAllowed);
23186             }
23187             
23188             if(this.afterDragEnter){
23189                 /**
23190                  * An empty function by default, but provided so that you can perform a custom action
23191                  * when the dragged item enters the drop target by providing an implementation.
23192                  * @param {Roo.dd.DragDrop} target The drop target
23193                  * @param {Event} e The event object
23194                  * @param {String} id The id of the dragged element
23195                  * @method afterDragEnter
23196                  */
23197                 this.afterDragEnter(target, e, id);
23198             }
23199         }
23200     },
23201
23202     /**
23203      * An empty function by default, but provided so that you can perform a custom action
23204      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23205      * @param {Roo.dd.DragDrop} target The drop target
23206      * @param {Event} e The event object
23207      * @param {String} id The id of the dragged element
23208      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23209      */
23210     beforeDragEnter : function(target, e, id){
23211         return true;
23212     },
23213
23214     // private
23215     alignElWithMouse: function() {
23216         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23217         this.proxy.sync();
23218     },
23219
23220     // private
23221     onDragOver : function(e, id){
23222         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23223         if(this.beforeDragOver(target, e, id) !== false){
23224             if(target.isNotifyTarget){
23225                 var status = target.notifyOver(this, e, this.dragData);
23226                 this.proxy.setStatus(status);
23227             }
23228
23229             if(this.afterDragOver){
23230                 /**
23231                  * An empty function by default, but provided so that you can perform a custom action
23232                  * while the dragged item is over the drop target by providing an implementation.
23233                  * @param {Roo.dd.DragDrop} target The drop target
23234                  * @param {Event} e The event object
23235                  * @param {String} id The id of the dragged element
23236                  * @method afterDragOver
23237                  */
23238                 this.afterDragOver(target, e, id);
23239             }
23240         }
23241     },
23242
23243     /**
23244      * An empty function by default, but provided so that you can perform a custom action
23245      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23246      * @param {Roo.dd.DragDrop} target The drop target
23247      * @param {Event} e The event object
23248      * @param {String} id The id of the dragged element
23249      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23250      */
23251     beforeDragOver : function(target, e, id){
23252         return true;
23253     },
23254
23255     // private
23256     onDragOut : function(e, id){
23257         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23258         if(this.beforeDragOut(target, e, id) !== false){
23259             if(target.isNotifyTarget){
23260                 target.notifyOut(this, e, this.dragData);
23261             }
23262             this.proxy.reset();
23263             if(this.afterDragOut){
23264                 /**
23265                  * An empty function by default, but provided so that you can perform a custom action
23266                  * after the dragged item is dragged out of the target without dropping.
23267                  * @param {Roo.dd.DragDrop} target The drop target
23268                  * @param {Event} e The event object
23269                  * @param {String} id The id of the dragged element
23270                  * @method afterDragOut
23271                  */
23272                 this.afterDragOut(target, e, id);
23273             }
23274         }
23275         this.cachedTarget = null;
23276     },
23277
23278     /**
23279      * An empty function by default, but provided so that you can perform a custom action before the dragged
23280      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23281      * @param {Roo.dd.DragDrop} target The drop target
23282      * @param {Event} e The event object
23283      * @param {String} id The id of the dragged element
23284      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23285      */
23286     beforeDragOut : function(target, e, id){
23287         return true;
23288     },
23289     
23290     // private
23291     onDragDrop : function(e, id){
23292         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23293         if(this.beforeDragDrop(target, e, id) !== false){
23294             if(target.isNotifyTarget){
23295                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23296                     this.onValidDrop(target, e, id);
23297                 }else{
23298                     this.onInvalidDrop(target, e, id);
23299                 }
23300             }else{
23301                 this.onValidDrop(target, e, id);
23302             }
23303             
23304             if(this.afterDragDrop){
23305                 /**
23306                  * An empty function by default, but provided so that you can perform a custom action
23307                  * after a valid drag drop has occurred by providing an implementation.
23308                  * @param {Roo.dd.DragDrop} target The drop target
23309                  * @param {Event} e The event object
23310                  * @param {String} id The id of the dropped element
23311                  * @method afterDragDrop
23312                  */
23313                 this.afterDragDrop(target, e, id);
23314             }
23315         }
23316         delete this.cachedTarget;
23317     },
23318
23319     /**
23320      * An empty function by default, but provided so that you can perform a custom action before the dragged
23321      * item is dropped onto the target and optionally cancel the onDragDrop.
23322      * @param {Roo.dd.DragDrop} target The drop target
23323      * @param {Event} e The event object
23324      * @param {String} id The id of the dragged element
23325      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23326      */
23327     beforeDragDrop : function(target, e, id){
23328         return true;
23329     },
23330
23331     // private
23332     onValidDrop : function(target, e, id){
23333         this.hideProxy();
23334         if(this.afterValidDrop){
23335             /**
23336              * An empty function by default, but provided so that you can perform a custom action
23337              * after a valid drop has occurred by providing an implementation.
23338              * @param {Object} target The target DD 
23339              * @param {Event} e The event object
23340              * @param {String} id The id of the dropped element
23341              * @method afterInvalidDrop
23342              */
23343             this.afterValidDrop(target, e, id);
23344         }
23345     },
23346
23347     // private
23348     getRepairXY : function(e, data){
23349         return this.el.getXY();  
23350     },
23351
23352     // private
23353     onInvalidDrop : function(target, e, id){
23354         this.beforeInvalidDrop(target, e, id);
23355         if(this.cachedTarget){
23356             if(this.cachedTarget.isNotifyTarget){
23357                 this.cachedTarget.notifyOut(this, e, this.dragData);
23358             }
23359             this.cacheTarget = null;
23360         }
23361         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23362
23363         if(this.afterInvalidDrop){
23364             /**
23365              * An empty function by default, but provided so that you can perform a custom action
23366              * after an invalid drop has occurred by providing an implementation.
23367              * @param {Event} e The event object
23368              * @param {String} id The id of the dropped element
23369              * @method afterInvalidDrop
23370              */
23371             this.afterInvalidDrop(e, id);
23372         }
23373     },
23374
23375     // private
23376     afterRepair : function(){
23377         if(Roo.enableFx){
23378             this.el.highlight(this.hlColor || "c3daf9");
23379         }
23380         this.dragging = false;
23381     },
23382
23383     /**
23384      * An empty function by default, but provided so that you can perform a custom action after an invalid
23385      * drop has occurred.
23386      * @param {Roo.dd.DragDrop} target The drop target
23387      * @param {Event} e The event object
23388      * @param {String} id The id of the dragged element
23389      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23390      */
23391     beforeInvalidDrop : function(target, e, id){
23392         return true;
23393     },
23394
23395     // private
23396     handleMouseDown : function(e){
23397         if(this.dragging) {
23398             return;
23399         }
23400         var data = this.getDragData(e);
23401         if(data && this.onBeforeDrag(data, e) !== false){
23402             this.dragData = data;
23403             this.proxy.stop();
23404             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23405         } 
23406     },
23407
23408     /**
23409      * An empty function by default, but provided so that you can perform a custom action before the initial
23410      * drag event begins and optionally cancel it.
23411      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23412      * @param {Event} e The event object
23413      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23414      */
23415     onBeforeDrag : function(data, e){
23416         return true;
23417     },
23418
23419     /**
23420      * An empty function by default, but provided so that you can perform a custom action once the initial
23421      * drag event has begun.  The drag cannot be canceled from this function.
23422      * @param {Number} x The x position of the click on the dragged object
23423      * @param {Number} y The y position of the click on the dragged object
23424      */
23425     onStartDrag : Roo.emptyFn,
23426
23427     // private - YUI override
23428     startDrag : function(x, y){
23429         this.proxy.reset();
23430         this.dragging = true;
23431         this.proxy.update("");
23432         this.onInitDrag(x, y);
23433         this.proxy.show();
23434     },
23435
23436     // private
23437     onInitDrag : function(x, y){
23438         var clone = this.el.dom.cloneNode(true);
23439         clone.id = Roo.id(); // prevent duplicate ids
23440         this.proxy.update(clone);
23441         this.onStartDrag(x, y);
23442         return true;
23443     },
23444
23445     /**
23446      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23447      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23448      */
23449     getProxy : function(){
23450         return this.proxy;  
23451     },
23452
23453     /**
23454      * Hides the drag source's {@link Roo.dd.StatusProxy}
23455      */
23456     hideProxy : function(){
23457         this.proxy.hide();  
23458         this.proxy.reset(true);
23459         this.dragging = false;
23460     },
23461
23462     // private
23463     triggerCacheRefresh : function(){
23464         Roo.dd.DDM.refreshCache(this.groups);
23465     },
23466
23467     // private - override to prevent hiding
23468     b4EndDrag: function(e) {
23469     },
23470
23471     // private - override to prevent moving
23472     endDrag : function(e){
23473         this.onEndDrag(this.dragData, e);
23474     },
23475
23476     // private
23477     onEndDrag : function(data, e){
23478     },
23479     
23480     // private - pin to cursor
23481     autoOffset : function(x, y) {
23482         this.setDelta(-12, -20);
23483     }    
23484 });/*
23485  * Based on:
23486  * Ext JS Library 1.1.1
23487  * Copyright(c) 2006-2007, Ext JS, LLC.
23488  *
23489  * Originally Released Under LGPL - original licence link has changed is not relivant.
23490  *
23491  * Fork - LGPL
23492  * <script type="text/javascript">
23493  */
23494
23495
23496 /**
23497  * @class Roo.dd.DropTarget
23498  * @extends Roo.dd.DDTarget
23499  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23500  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23501  * @constructor
23502  * @param {String/HTMLElement/Element} el The container element
23503  * @param {Object} config
23504  */
23505 Roo.dd.DropTarget = function(el, config){
23506     this.el = Roo.get(el);
23507     
23508     var listeners = false; ;
23509     if (config && config.listeners) {
23510         listeners= config.listeners;
23511         delete config.listeners;
23512     }
23513     Roo.apply(this, config);
23514     
23515     if(this.containerScroll){
23516         Roo.dd.ScrollManager.register(this.el);
23517     }
23518     this.addEvents( {
23519          /**
23520          * @scope Roo.dd.DropTarget
23521          */
23522          
23523          /**
23524          * @event enter
23525          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23526          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23527          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23528          * 
23529          * IMPORTANT : it should set  this.valid to true|false
23530          * 
23531          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23532          * @param {Event} e The event
23533          * @param {Object} data An object containing arbitrary data supplied by the drag source
23534          */
23535         "enter" : true,
23536         
23537          /**
23538          * @event over
23539          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23540          * This method will be called on every mouse movement while the drag source is over the drop target.
23541          * This default implementation simply returns the dropAllowed config value.
23542          * 
23543          * IMPORTANT : it should set  this.valid to true|false
23544          * 
23545          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23546          * @param {Event} e The event
23547          * @param {Object} data An object containing arbitrary data supplied by the drag source
23548          
23549          */
23550         "over" : true,
23551         /**
23552          * @event out
23553          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23554          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23555          * overClass (if any) from the drop element.
23556          * 
23557          * 
23558          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23559          * @param {Event} e The event
23560          * @param {Object} data An object containing arbitrary data supplied by the drag source
23561          */
23562          "out" : true,
23563          
23564         /**
23565          * @event drop
23566          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23567          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23568          * implementation that does something to process the drop event and returns true so that the drag source's
23569          * repair action does not run.
23570          * 
23571          * IMPORTANT : it should set this.success
23572          * 
23573          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23574          * @param {Event} e The event
23575          * @param {Object} data An object containing arbitrary data supplied by the drag source
23576         */
23577          "drop" : true
23578     });
23579             
23580      
23581     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23582         this.el.dom, 
23583         this.ddGroup || this.group,
23584         {
23585             isTarget: true,
23586             listeners : listeners || {} 
23587            
23588         
23589         }
23590     );
23591
23592 };
23593
23594 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23595     /**
23596      * @cfg {String} overClass
23597      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23598      */
23599      /**
23600      * @cfg {String} ddGroup
23601      * The drag drop group to handle drop events for
23602      */
23603      
23604     /**
23605      * @cfg {String} dropAllowed
23606      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23607      */
23608     dropAllowed : "x-dd-drop-ok",
23609     /**
23610      * @cfg {String} dropNotAllowed
23611      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23612      */
23613     dropNotAllowed : "x-dd-drop-nodrop",
23614     /**
23615      * @cfg {boolean} success
23616      * set this after drop listener.. 
23617      */
23618     success : false,
23619     /**
23620      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23621      * if the drop point is valid for over/enter..
23622      */
23623     valid : false,
23624     // private
23625     isTarget : true,
23626
23627     // private
23628     isNotifyTarget : true,
23629     
23630     /**
23631      * @hide
23632      */
23633     notifyEnter : function(dd, e, data)
23634     {
23635         this.valid = true;
23636         this.fireEvent('enter', dd, e, data);
23637         if(this.overClass){
23638             this.el.addClass(this.overClass);
23639         }
23640         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23641             this.valid ? this.dropAllowed : this.dropNotAllowed
23642         );
23643     },
23644
23645     /**
23646      * @hide
23647      */
23648     notifyOver : function(dd, e, data)
23649     {
23650         this.valid = true;
23651         this.fireEvent('over', dd, e, data);
23652         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23653             this.valid ? this.dropAllowed : this.dropNotAllowed
23654         );
23655     },
23656
23657     /**
23658      * @hide
23659      */
23660     notifyOut : function(dd, e, data)
23661     {
23662         this.fireEvent('out', dd, e, data);
23663         if(this.overClass){
23664             this.el.removeClass(this.overClass);
23665         }
23666     },
23667
23668     /**
23669      * @hide
23670      */
23671     notifyDrop : function(dd, e, data)
23672     {
23673         this.success = false;
23674         this.fireEvent('drop', dd, e, data);
23675         return this.success;
23676     }
23677 });/*
23678  * Based on:
23679  * Ext JS Library 1.1.1
23680  * Copyright(c) 2006-2007, Ext JS, LLC.
23681  *
23682  * Originally Released Under LGPL - original licence link has changed is not relivant.
23683  *
23684  * Fork - LGPL
23685  * <script type="text/javascript">
23686  */
23687
23688
23689 /**
23690  * @class Roo.dd.DragZone
23691  * @extends Roo.dd.DragSource
23692  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23693  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23694  * @constructor
23695  * @param {String/HTMLElement/Element} el The container element
23696  * @param {Object} config
23697  */
23698 Roo.dd.DragZone = function(el, config){
23699     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23700     if(this.containerScroll){
23701         Roo.dd.ScrollManager.register(this.el);
23702     }
23703 };
23704
23705 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23706     /**
23707      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23708      * for auto scrolling during drag operations.
23709      */
23710     /**
23711      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23712      * method after a failed drop (defaults to "c3daf9" - light blue)
23713      */
23714
23715     /**
23716      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23717      * for a valid target to drag based on the mouse down. Override this method
23718      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23719      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23720      * @param {EventObject} e The mouse down event
23721      * @return {Object} The dragData
23722      */
23723     getDragData : function(e){
23724         return Roo.dd.Registry.getHandleFromEvent(e);
23725     },
23726     
23727     /**
23728      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23729      * this.dragData.ddel
23730      * @param {Number} x The x position of the click on the dragged object
23731      * @param {Number} y The y position of the click on the dragged object
23732      * @return {Boolean} true to continue the drag, false to cancel
23733      */
23734     onInitDrag : function(x, y){
23735         this.proxy.update(this.dragData.ddel.cloneNode(true));
23736         this.onStartDrag(x, y);
23737         return true;
23738     },
23739     
23740     /**
23741      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23742      */
23743     afterRepair : function(){
23744         if(Roo.enableFx){
23745             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23746         }
23747         this.dragging = false;
23748     },
23749
23750     /**
23751      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23752      * the XY of this.dragData.ddel
23753      * @param {EventObject} e The mouse up event
23754      * @return {Array} The xy location (e.g. [100, 200])
23755      */
23756     getRepairXY : function(e){
23757         return Roo.Element.fly(this.dragData.ddel).getXY();  
23758     }
23759 });/*
23760  * Based on:
23761  * Ext JS Library 1.1.1
23762  * Copyright(c) 2006-2007, Ext JS, LLC.
23763  *
23764  * Originally Released Under LGPL - original licence link has changed is not relivant.
23765  *
23766  * Fork - LGPL
23767  * <script type="text/javascript">
23768  */
23769 /**
23770  * @class Roo.dd.DropZone
23771  * @extends Roo.dd.DropTarget
23772  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23773  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23774  * @constructor
23775  * @param {String/HTMLElement/Element} el The container element
23776  * @param {Object} config
23777  */
23778 Roo.dd.DropZone = function(el, config){
23779     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23780 };
23781
23782 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23783     /**
23784      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23785      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23786      * provide your own custom lookup.
23787      * @param {Event} e The event
23788      * @return {Object} data The custom data
23789      */
23790     getTargetFromEvent : function(e){
23791         return Roo.dd.Registry.getTargetFromEvent(e);
23792     },
23793
23794     /**
23795      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23796      * that it has registered.  This method has no default implementation and should be overridden to provide
23797      * node-specific processing if necessary.
23798      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23799      * {@link #getTargetFromEvent} for this node)
23800      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23801      * @param {Event} e The event
23802      * @param {Object} data An object containing arbitrary data supplied by the drag source
23803      */
23804     onNodeEnter : function(n, dd, e, data){
23805         
23806     },
23807
23808     /**
23809      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23810      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23811      * overridden to provide the proper feedback.
23812      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23813      * {@link #getTargetFromEvent} for this node)
23814      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23815      * @param {Event} e The event
23816      * @param {Object} data An object containing arbitrary data supplied by the drag source
23817      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23818      * underlying {@link Roo.dd.StatusProxy} can be updated
23819      */
23820     onNodeOver : function(n, dd, e, data){
23821         return this.dropAllowed;
23822     },
23823
23824     /**
23825      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23826      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23827      * node-specific processing if necessary.
23828      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23829      * {@link #getTargetFromEvent} for this node)
23830      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23831      * @param {Event} e The event
23832      * @param {Object} data An object containing arbitrary data supplied by the drag source
23833      */
23834     onNodeOut : function(n, dd, e, data){
23835         
23836     },
23837
23838     /**
23839      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23840      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23841      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23842      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23843      * {@link #getTargetFromEvent} for this node)
23844      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23845      * @param {Event} e The event
23846      * @param {Object} data An object containing arbitrary data supplied by the drag source
23847      * @return {Boolean} True if the drop was valid, else false
23848      */
23849     onNodeDrop : function(n, dd, e, data){
23850         return false;
23851     },
23852
23853     /**
23854      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23855      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23856      * it should be overridden to provide the proper feedback if necessary.
23857      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23858      * @param {Event} e The event
23859      * @param {Object} data An object containing arbitrary data supplied by the drag source
23860      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23861      * underlying {@link Roo.dd.StatusProxy} can be updated
23862      */
23863     onContainerOver : function(dd, e, data){
23864         return this.dropNotAllowed;
23865     },
23866
23867     /**
23868      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23869      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23870      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23871      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23872      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23873      * @param {Event} e The event
23874      * @param {Object} data An object containing arbitrary data supplied by the drag source
23875      * @return {Boolean} True if the drop was valid, else false
23876      */
23877     onContainerDrop : function(dd, e, data){
23878         return false;
23879     },
23880
23881     /**
23882      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23883      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23884      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23885      * you should override this method and provide a custom implementation.
23886      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23887      * @param {Event} e The event
23888      * @param {Object} data An object containing arbitrary data supplied by the drag source
23889      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23890      * underlying {@link Roo.dd.StatusProxy} can be updated
23891      */
23892     notifyEnter : function(dd, e, data){
23893         return this.dropNotAllowed;
23894     },
23895
23896     /**
23897      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23898      * This method will be called on every mouse movement while the drag source is over the drop zone.
23899      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23900      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23901      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23902      * registered node, it will call {@link #onContainerOver}.
23903      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23904      * @param {Event} e The event
23905      * @param {Object} data An object containing arbitrary data supplied by the drag source
23906      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23907      * underlying {@link Roo.dd.StatusProxy} can be updated
23908      */
23909     notifyOver : function(dd, e, data){
23910         var n = this.getTargetFromEvent(e);
23911         if(!n){ // not over valid drop target
23912             if(this.lastOverNode){
23913                 this.onNodeOut(this.lastOverNode, dd, e, data);
23914                 this.lastOverNode = null;
23915             }
23916             return this.onContainerOver(dd, e, data);
23917         }
23918         if(this.lastOverNode != n){
23919             if(this.lastOverNode){
23920                 this.onNodeOut(this.lastOverNode, dd, e, data);
23921             }
23922             this.onNodeEnter(n, dd, e, data);
23923             this.lastOverNode = n;
23924         }
23925         return this.onNodeOver(n, dd, e, data);
23926     },
23927
23928     /**
23929      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23930      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23931      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23932      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23933      * @param {Event} e The event
23934      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23935      */
23936     notifyOut : function(dd, e, data){
23937         if(this.lastOverNode){
23938             this.onNodeOut(this.lastOverNode, dd, e, data);
23939             this.lastOverNode = null;
23940         }
23941     },
23942
23943     /**
23944      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23945      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23946      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23947      * otherwise it will call {@link #onContainerDrop}.
23948      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23949      * @param {Event} e The event
23950      * @param {Object} data An object containing arbitrary data supplied by the drag source
23951      * @return {Boolean} True if the drop was valid, else false
23952      */
23953     notifyDrop : function(dd, e, data){
23954         if(this.lastOverNode){
23955             this.onNodeOut(this.lastOverNode, dd, e, data);
23956             this.lastOverNode = null;
23957         }
23958         var n = this.getTargetFromEvent(e);
23959         return n ?
23960             this.onNodeDrop(n, dd, e, data) :
23961             this.onContainerDrop(dd, e, data);
23962     },
23963
23964     // private
23965     triggerCacheRefresh : function(){
23966         Roo.dd.DDM.refreshCache(this.groups);
23967     }  
23968 });/*
23969  * Based on:
23970  * Ext JS Library 1.1.1
23971  * Copyright(c) 2006-2007, Ext JS, LLC.
23972  *
23973  * Originally Released Under LGPL - original licence link has changed is not relivant.
23974  *
23975  * Fork - LGPL
23976  * <script type="text/javascript">
23977  */
23978
23979
23980 /**
23981  * @class Roo.data.SortTypes
23982  * @static
23983  * Defines the default sorting (casting?) comparison functions used when sorting data.
23984  */
23985 Roo.data.SortTypes = {
23986     /**
23987      * Default sort that does nothing
23988      * @param {Mixed} s The value being converted
23989      * @return {Mixed} The comparison value
23990      */
23991     none : function(s){
23992         return s;
23993     },
23994     
23995     /**
23996      * The regular expression used to strip tags
23997      * @type {RegExp}
23998      * @property
23999      */
24000     stripTagsRE : /<\/?[^>]+>/gi,
24001     
24002     /**
24003      * Strips all HTML tags to sort on text only
24004      * @param {Mixed} s The value being converted
24005      * @return {String} The comparison value
24006      */
24007     asText : function(s){
24008         return String(s).replace(this.stripTagsRE, "");
24009     },
24010     
24011     /**
24012      * Strips all HTML tags to sort on text only - Case insensitive
24013      * @param {Mixed} s The value being converted
24014      * @return {String} The comparison value
24015      */
24016     asUCText : function(s){
24017         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24018     },
24019     
24020     /**
24021      * Case insensitive string
24022      * @param {Mixed} s The value being converted
24023      * @return {String} The comparison value
24024      */
24025     asUCString : function(s) {
24026         return String(s).toUpperCase();
24027     },
24028     
24029     /**
24030      * Date sorting
24031      * @param {Mixed} s The value being converted
24032      * @return {Number} The comparison value
24033      */
24034     asDate : function(s) {
24035         if(!s){
24036             return 0;
24037         }
24038         if(s instanceof Date){
24039             return s.getTime();
24040         }
24041         return Date.parse(String(s));
24042     },
24043     
24044     /**
24045      * Float sorting
24046      * @param {Mixed} s The value being converted
24047      * @return {Float} The comparison value
24048      */
24049     asFloat : function(s) {
24050         var val = parseFloat(String(s).replace(/,/g, ""));
24051         if(isNaN(val)) {
24052             val = 0;
24053         }
24054         return val;
24055     },
24056     
24057     /**
24058      * Integer sorting
24059      * @param {Mixed} s The value being converted
24060      * @return {Number} The comparison value
24061      */
24062     asInt : function(s) {
24063         var val = parseInt(String(s).replace(/,/g, ""));
24064         if(isNaN(val)) {
24065             val = 0;
24066         }
24067         return val;
24068     }
24069 };/*
24070  * Based on:
24071  * Ext JS Library 1.1.1
24072  * Copyright(c) 2006-2007, Ext JS, LLC.
24073  *
24074  * Originally Released Under LGPL - original licence link has changed is not relivant.
24075  *
24076  * Fork - LGPL
24077  * <script type="text/javascript">
24078  */
24079
24080 /**
24081 * @class Roo.data.Record
24082  * Instances of this class encapsulate both record <em>definition</em> information, and record
24083  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24084  * to access Records cached in an {@link Roo.data.Store} object.<br>
24085  * <p>
24086  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24087  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24088  * objects.<br>
24089  * <p>
24090  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24091  * @constructor
24092  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24093  * {@link #create}. The parameters are the same.
24094  * @param {Array} data An associative Array of data values keyed by the field name.
24095  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24096  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24097  * not specified an integer id is generated.
24098  */
24099 Roo.data.Record = function(data, id){
24100     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24101     this.data = data;
24102 };
24103
24104 /**
24105  * Generate a constructor for a specific record layout.
24106  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24107  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24108  * Each field definition object may contain the following properties: <ul>
24109  * <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,
24110  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24111  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24112  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24113  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24114  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24115  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24116  * this may be omitted.</p></li>
24117  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24118  * <ul><li>auto (Default, implies no conversion)</li>
24119  * <li>string</li>
24120  * <li>int</li>
24121  * <li>float</li>
24122  * <li>boolean</li>
24123  * <li>date</li></ul></p></li>
24124  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24125  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24126  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24127  * by the Reader into an object that will be stored in the Record. It is passed the
24128  * following parameters:<ul>
24129  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24130  * </ul></p></li>
24131  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24132  * </ul>
24133  * <br>usage:<br><pre><code>
24134 var TopicRecord = Roo.data.Record.create(
24135     {name: 'title', mapping: 'topic_title'},
24136     {name: 'author', mapping: 'username'},
24137     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24138     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24139     {name: 'lastPoster', mapping: 'user2'},
24140     {name: 'excerpt', mapping: 'post_text'}
24141 );
24142
24143 var myNewRecord = new TopicRecord({
24144     title: 'Do my job please',
24145     author: 'noobie',
24146     totalPosts: 1,
24147     lastPost: new Date(),
24148     lastPoster: 'Animal',
24149     excerpt: 'No way dude!'
24150 });
24151 myStore.add(myNewRecord);
24152 </code></pre>
24153  * @method create
24154  * @static
24155  */
24156 Roo.data.Record.create = function(o){
24157     var f = function(){
24158         f.superclass.constructor.apply(this, arguments);
24159     };
24160     Roo.extend(f, Roo.data.Record);
24161     var p = f.prototype;
24162     p.fields = new Roo.util.MixedCollection(false, function(field){
24163         return field.name;
24164     });
24165     for(var i = 0, len = o.length; i < len; i++){
24166         p.fields.add(new Roo.data.Field(o[i]));
24167     }
24168     f.getField = function(name){
24169         return p.fields.get(name);  
24170     };
24171     return f;
24172 };
24173
24174 Roo.data.Record.AUTO_ID = 1000;
24175 Roo.data.Record.EDIT = 'edit';
24176 Roo.data.Record.REJECT = 'reject';
24177 Roo.data.Record.COMMIT = 'commit';
24178
24179 Roo.data.Record.prototype = {
24180     /**
24181      * Readonly flag - true if this record has been modified.
24182      * @type Boolean
24183      */
24184     dirty : false,
24185     editing : false,
24186     error: null,
24187     modified: null,
24188
24189     // private
24190     join : function(store){
24191         this.store = store;
24192     },
24193
24194     /**
24195      * Set the named field to the specified value.
24196      * @param {String} name The name of the field to set.
24197      * @param {Object} value The value to set the field to.
24198      */
24199     set : function(name, value){
24200         if(this.data[name] == value){
24201             return;
24202         }
24203         this.dirty = true;
24204         if(!this.modified){
24205             this.modified = {};
24206         }
24207         if(typeof this.modified[name] == 'undefined'){
24208             this.modified[name] = this.data[name];
24209         }
24210         this.data[name] = value;
24211         if(!this.editing && this.store){
24212             this.store.afterEdit(this);
24213         }       
24214     },
24215
24216     /**
24217      * Get the value of the named field.
24218      * @param {String} name The name of the field to get the value of.
24219      * @return {Object} The value of the field.
24220      */
24221     get : function(name){
24222         return this.data[name]; 
24223     },
24224
24225     // private
24226     beginEdit : function(){
24227         this.editing = true;
24228         this.modified = {}; 
24229     },
24230
24231     // private
24232     cancelEdit : function(){
24233         this.editing = false;
24234         delete this.modified;
24235     },
24236
24237     // private
24238     endEdit : function(){
24239         this.editing = false;
24240         if(this.dirty && this.store){
24241             this.store.afterEdit(this);
24242         }
24243     },
24244
24245     /**
24246      * Usually called by the {@link Roo.data.Store} which owns the Record.
24247      * Rejects all changes made to the Record since either creation, or the last commit operation.
24248      * Modified fields are reverted to their original values.
24249      * <p>
24250      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24251      * of reject operations.
24252      */
24253     reject : function(){
24254         var m = this.modified;
24255         for(var n in m){
24256             if(typeof m[n] != "function"){
24257                 this.data[n] = m[n];
24258             }
24259         }
24260         this.dirty = false;
24261         delete this.modified;
24262         this.editing = false;
24263         if(this.store){
24264             this.store.afterReject(this);
24265         }
24266     },
24267
24268     /**
24269      * Usually called by the {@link Roo.data.Store} which owns the Record.
24270      * Commits all changes made to the Record since either creation, or the last commit operation.
24271      * <p>
24272      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24273      * of commit operations.
24274      */
24275     commit : function(){
24276         this.dirty = false;
24277         delete this.modified;
24278         this.editing = false;
24279         if(this.store){
24280             this.store.afterCommit(this);
24281         }
24282     },
24283
24284     // private
24285     hasError : function(){
24286         return this.error != null;
24287     },
24288
24289     // private
24290     clearError : function(){
24291         this.error = null;
24292     },
24293
24294     /**
24295      * Creates a copy of this record.
24296      * @param {String} id (optional) A new record id if you don't want to use this record's id
24297      * @return {Record}
24298      */
24299     copy : function(newId) {
24300         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24301     }
24302 };/*
24303  * Based on:
24304  * Ext JS Library 1.1.1
24305  * Copyright(c) 2006-2007, Ext JS, LLC.
24306  *
24307  * Originally Released Under LGPL - original licence link has changed is not relivant.
24308  *
24309  * Fork - LGPL
24310  * <script type="text/javascript">
24311  */
24312
24313
24314
24315 /**
24316  * @class Roo.data.Store
24317  * @extends Roo.util.Observable
24318  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24319  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24320  * <p>
24321  * 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
24322  * has no knowledge of the format of the data returned by the Proxy.<br>
24323  * <p>
24324  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24325  * instances from the data object. These records are cached and made available through accessor functions.
24326  * @constructor
24327  * Creates a new Store.
24328  * @param {Object} config A config object containing the objects needed for the Store to access data,
24329  * and read the data into Records.
24330  */
24331 Roo.data.Store = function(config){
24332     this.data = new Roo.util.MixedCollection(false);
24333     this.data.getKey = function(o){
24334         return o.id;
24335     };
24336     this.baseParams = {};
24337     // private
24338     this.paramNames = {
24339         "start" : "start",
24340         "limit" : "limit",
24341         "sort" : "sort",
24342         "dir" : "dir",
24343         "multisort" : "_multisort"
24344     };
24345
24346     if(config && config.data){
24347         this.inlineData = config.data;
24348         delete config.data;
24349     }
24350
24351     Roo.apply(this, config);
24352     
24353     if(this.reader){ // reader passed
24354         this.reader = Roo.factory(this.reader, Roo.data);
24355         this.reader.xmodule = this.xmodule || false;
24356         if(!this.recordType){
24357             this.recordType = this.reader.recordType;
24358         }
24359         if(this.reader.onMetaChange){
24360             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24361         }
24362     }
24363
24364     if(this.recordType){
24365         this.fields = this.recordType.prototype.fields;
24366     }
24367     this.modified = [];
24368
24369     this.addEvents({
24370         /**
24371          * @event datachanged
24372          * Fires when the data cache has changed, and a widget which is using this Store
24373          * as a Record cache should refresh its view.
24374          * @param {Store} this
24375          */
24376         datachanged : true,
24377         /**
24378          * @event metachange
24379          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24380          * @param {Store} this
24381          * @param {Object} meta The JSON metadata
24382          */
24383         metachange : true,
24384         /**
24385          * @event add
24386          * Fires when Records have been added to the Store
24387          * @param {Store} this
24388          * @param {Roo.data.Record[]} records The array of Records added
24389          * @param {Number} index The index at which the record(s) were added
24390          */
24391         add : true,
24392         /**
24393          * @event remove
24394          * Fires when a Record has been removed from the Store
24395          * @param {Store} this
24396          * @param {Roo.data.Record} record The Record that was removed
24397          * @param {Number} index The index at which the record was removed
24398          */
24399         remove : true,
24400         /**
24401          * @event update
24402          * Fires when a Record has been updated
24403          * @param {Store} this
24404          * @param {Roo.data.Record} record The Record that was updated
24405          * @param {String} operation The update operation being performed.  Value may be one of:
24406          * <pre><code>
24407  Roo.data.Record.EDIT
24408  Roo.data.Record.REJECT
24409  Roo.data.Record.COMMIT
24410          * </code></pre>
24411          */
24412         update : true,
24413         /**
24414          * @event clear
24415          * Fires when the data cache has been cleared.
24416          * @param {Store} this
24417          */
24418         clear : true,
24419         /**
24420          * @event beforeload
24421          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24422          * the load action will be canceled.
24423          * @param {Store} this
24424          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24425          */
24426         beforeload : true,
24427         /**
24428          * @event beforeloadadd
24429          * Fires after a new set of Records has been loaded.
24430          * @param {Store} this
24431          * @param {Roo.data.Record[]} records The Records that were loaded
24432          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24433          */
24434         beforeloadadd : true,
24435         /**
24436          * @event load
24437          * Fires after a new set of Records has been loaded, before they are added to the store.
24438          * @param {Store} this
24439          * @param {Roo.data.Record[]} records The Records that were loaded
24440          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24441          * @params {Object} return from reader
24442          */
24443         load : true,
24444         /**
24445          * @event loadexception
24446          * Fires if an exception occurs in the Proxy during loading.
24447          * Called with the signature of the Proxy's "loadexception" event.
24448          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24449          * 
24450          * @param {Proxy} 
24451          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24452          * @param {Object} load options 
24453          * @param {Object} jsonData from your request (normally this contains the Exception)
24454          */
24455         loadexception : true
24456     });
24457     
24458     if(this.proxy){
24459         this.proxy = Roo.factory(this.proxy, Roo.data);
24460         this.proxy.xmodule = this.xmodule || false;
24461         this.relayEvents(this.proxy,  ["loadexception"]);
24462     }
24463     this.sortToggle = {};
24464     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24465
24466     Roo.data.Store.superclass.constructor.call(this);
24467
24468     if(this.inlineData){
24469         this.loadData(this.inlineData);
24470         delete this.inlineData;
24471     }
24472 };
24473
24474 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24475      /**
24476     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24477     * without a remote query - used by combo/forms at present.
24478     */
24479     
24480     /**
24481     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24482     */
24483     /**
24484     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24485     */
24486     /**
24487     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24488     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24489     */
24490     /**
24491     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24492     * on any HTTP request
24493     */
24494     /**
24495     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24496     */
24497     /**
24498     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24499     */
24500     multiSort: false,
24501     /**
24502     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24503     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24504     */
24505     remoteSort : false,
24506
24507     /**
24508     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24509      * loaded or when a record is removed. (defaults to false).
24510     */
24511     pruneModifiedRecords : false,
24512
24513     // private
24514     lastOptions : null,
24515
24516     /**
24517      * Add Records to the Store and fires the add event.
24518      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24519      */
24520     add : function(records){
24521         records = [].concat(records);
24522         for(var i = 0, len = records.length; i < len; i++){
24523             records[i].join(this);
24524         }
24525         var index = this.data.length;
24526         this.data.addAll(records);
24527         this.fireEvent("add", this, records, index);
24528     },
24529
24530     /**
24531      * Remove a Record from the Store and fires the remove event.
24532      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24533      */
24534     remove : function(record){
24535         var index = this.data.indexOf(record);
24536         this.data.removeAt(index);
24537  
24538         if(this.pruneModifiedRecords){
24539             this.modified.remove(record);
24540         }
24541         this.fireEvent("remove", this, record, index);
24542     },
24543
24544     /**
24545      * Remove all Records from the Store and fires the clear event.
24546      */
24547     removeAll : function(){
24548         this.data.clear();
24549         if(this.pruneModifiedRecords){
24550             this.modified = [];
24551         }
24552         this.fireEvent("clear", this);
24553     },
24554
24555     /**
24556      * Inserts Records to the Store at the given index and fires the add event.
24557      * @param {Number} index The start index at which to insert the passed Records.
24558      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24559      */
24560     insert : function(index, records){
24561         records = [].concat(records);
24562         for(var i = 0, len = records.length; i < len; i++){
24563             this.data.insert(index, records[i]);
24564             records[i].join(this);
24565         }
24566         this.fireEvent("add", this, records, index);
24567     },
24568
24569     /**
24570      * Get the index within the cache of the passed Record.
24571      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24572      * @return {Number} The index of the passed Record. Returns -1 if not found.
24573      */
24574     indexOf : function(record){
24575         return this.data.indexOf(record);
24576     },
24577
24578     /**
24579      * Get the index within the cache of the Record with the passed id.
24580      * @param {String} id The id of the Record to find.
24581      * @return {Number} The index of the Record. Returns -1 if not found.
24582      */
24583     indexOfId : function(id){
24584         return this.data.indexOfKey(id);
24585     },
24586
24587     /**
24588      * Get the Record with the specified id.
24589      * @param {String} id The id of the Record to find.
24590      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24591      */
24592     getById : function(id){
24593         return this.data.key(id);
24594     },
24595
24596     /**
24597      * Get the Record at the specified index.
24598      * @param {Number} index The index of the Record to find.
24599      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24600      */
24601     getAt : function(index){
24602         return this.data.itemAt(index);
24603     },
24604
24605     /**
24606      * Returns a range of Records between specified indices.
24607      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24608      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24609      * @return {Roo.data.Record[]} An array of Records
24610      */
24611     getRange : function(start, end){
24612         return this.data.getRange(start, end);
24613     },
24614
24615     // private
24616     storeOptions : function(o){
24617         o = Roo.apply({}, o);
24618         delete o.callback;
24619         delete o.scope;
24620         this.lastOptions = o;
24621     },
24622
24623     /**
24624      * Loads the Record cache from the configured Proxy using the configured Reader.
24625      * <p>
24626      * If using remote paging, then the first load call must specify the <em>start</em>
24627      * and <em>limit</em> properties in the options.params property to establish the initial
24628      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24629      * <p>
24630      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24631      * and this call will return before the new data has been loaded. Perform any post-processing
24632      * in a callback function, or in a "load" event handler.</strong>
24633      * <p>
24634      * @param {Object} options An object containing properties which control loading options:<ul>
24635      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24636      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24637      * passed the following arguments:<ul>
24638      * <li>r : Roo.data.Record[]</li>
24639      * <li>options: Options object from the load call</li>
24640      * <li>success: Boolean success indicator</li></ul></li>
24641      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24642      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24643      * </ul>
24644      */
24645     load : function(options){
24646         options = options || {};
24647         if(this.fireEvent("beforeload", this, options) !== false){
24648             this.storeOptions(options);
24649             var p = Roo.apply(options.params || {}, this.baseParams);
24650             // if meta was not loaded from remote source.. try requesting it.
24651             if (!this.reader.metaFromRemote) {
24652                 p._requestMeta = 1;
24653             }
24654             if(this.sortInfo && this.remoteSort){
24655                 var pn = this.paramNames;
24656                 p[pn["sort"]] = this.sortInfo.field;
24657                 p[pn["dir"]] = this.sortInfo.direction;
24658             }
24659             if (this.multiSort) {
24660                 var pn = this.paramNames;
24661                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24662             }
24663             
24664             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24665         }
24666     },
24667
24668     /**
24669      * Reloads the Record cache from the configured Proxy using the configured Reader and
24670      * the options from the last load operation performed.
24671      * @param {Object} options (optional) An object containing properties which may override the options
24672      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24673      * the most recently used options are reused).
24674      */
24675     reload : function(options){
24676         this.load(Roo.applyIf(options||{}, this.lastOptions));
24677     },
24678
24679     // private
24680     // Called as a callback by the Reader during a load operation.
24681     loadRecords : function(o, options, success){
24682          
24683         if(!o){
24684             if(success !== false){
24685                 this.fireEvent("load", this, [], options, o);
24686             }
24687             if(options.callback){
24688                 options.callback.call(options.scope || this, [], options, false);
24689             }
24690             return;
24691         }
24692         // if data returned failure - throw an exception.
24693         if (o.success === false) {
24694             // show a message if no listener is registered.
24695             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24696                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24697             }
24698             // loadmask wil be hooked into this..
24699             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24700             return;
24701         }
24702         var r = o.records, t = o.totalRecords || r.length;
24703         
24704         this.fireEvent("beforeloadadd", this, r, options, o);
24705         
24706         if(!options || options.add !== true){
24707             if(this.pruneModifiedRecords){
24708                 this.modified = [];
24709             }
24710             for(var i = 0, len = r.length; i < len; i++){
24711                 r[i].join(this);
24712             }
24713             if(this.snapshot){
24714                 this.data = this.snapshot;
24715                 delete this.snapshot;
24716             }
24717             this.data.clear();
24718             this.data.addAll(r);
24719             this.totalLength = t;
24720             this.applySort();
24721             this.fireEvent("datachanged", this);
24722         }else{
24723             this.totalLength = Math.max(t, this.data.length+r.length);
24724             this.add(r);
24725         }
24726         
24727         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24728                 
24729             var e = new Roo.data.Record({});
24730
24731             e.set(this.parent.displayField, this.parent.emptyTitle);
24732             e.set(this.parent.valueField, '');
24733
24734             this.insert(0, e);
24735         }
24736             
24737         this.fireEvent("load", this, r, options, o);
24738         if(options.callback){
24739             options.callback.call(options.scope || this, r, options, true);
24740         }
24741     },
24742
24743
24744     /**
24745      * Loads data from a passed data block. A Reader which understands the format of the data
24746      * must have been configured in the constructor.
24747      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24748      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24749      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24750      */
24751     loadData : function(o, append){
24752         var r = this.reader.readRecords(o);
24753         this.loadRecords(r, {add: append}, true);
24754     },
24755     
24756      /**
24757      * using 'cn' the nested child reader read the child array into it's child stores.
24758      * @param {Object} rec The record with a 'children array
24759      */
24760     loadDataFromChildren : function(rec)
24761     {
24762         this.loadData(this.reader.toLoadData(rec));
24763     },
24764     
24765
24766     /**
24767      * Gets the number of cached records.
24768      * <p>
24769      * <em>If using paging, this may not be the total size of the dataset. If the data object
24770      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24771      * the data set size</em>
24772      */
24773     getCount : function(){
24774         return this.data.length || 0;
24775     },
24776
24777     /**
24778      * Gets the total number of records in the dataset as returned by the server.
24779      * <p>
24780      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24781      * the dataset size</em>
24782      */
24783     getTotalCount : function(){
24784         return this.totalLength || 0;
24785     },
24786
24787     /**
24788      * Returns the sort state of the Store as an object with two properties:
24789      * <pre><code>
24790  field {String} The name of the field by which the Records are sorted
24791  direction {String} The sort order, "ASC" or "DESC"
24792      * </code></pre>
24793      */
24794     getSortState : function(){
24795         return this.sortInfo;
24796     },
24797
24798     // private
24799     applySort : function(){
24800         if(this.sortInfo && !this.remoteSort){
24801             var s = this.sortInfo, f = s.field;
24802             var st = this.fields.get(f).sortType;
24803             var fn = function(r1, r2){
24804                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24805                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24806             };
24807             this.data.sort(s.direction, fn);
24808             if(this.snapshot && this.snapshot != this.data){
24809                 this.snapshot.sort(s.direction, fn);
24810             }
24811         }
24812     },
24813
24814     /**
24815      * Sets the default sort column and order to be used by the next load operation.
24816      * @param {String} fieldName The name of the field to sort by.
24817      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24818      */
24819     setDefaultSort : function(field, dir){
24820         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24821     },
24822
24823     /**
24824      * Sort the Records.
24825      * If remote sorting is used, the sort is performed on the server, and the cache is
24826      * reloaded. If local sorting is used, the cache is sorted internally.
24827      * @param {String} fieldName The name of the field to sort by.
24828      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24829      */
24830     sort : function(fieldName, dir){
24831         var f = this.fields.get(fieldName);
24832         if(!dir){
24833             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24834             
24835             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24836                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24837             }else{
24838                 dir = f.sortDir;
24839             }
24840         }
24841         this.sortToggle[f.name] = dir;
24842         this.sortInfo = {field: f.name, direction: dir};
24843         if(!this.remoteSort){
24844             this.applySort();
24845             this.fireEvent("datachanged", this);
24846         }else{
24847             this.load(this.lastOptions);
24848         }
24849     },
24850
24851     /**
24852      * Calls the specified function for each of the Records in the cache.
24853      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24854      * Returning <em>false</em> aborts and exits the iteration.
24855      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24856      */
24857     each : function(fn, scope){
24858         this.data.each(fn, scope);
24859     },
24860
24861     /**
24862      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24863      * (e.g., during paging).
24864      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24865      */
24866     getModifiedRecords : function(){
24867         return this.modified;
24868     },
24869
24870     // private
24871     createFilterFn : function(property, value, anyMatch){
24872         if(!value.exec){ // not a regex
24873             value = String(value);
24874             if(value.length == 0){
24875                 return false;
24876             }
24877             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24878         }
24879         return function(r){
24880             return value.test(r.data[property]);
24881         };
24882     },
24883
24884     /**
24885      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24886      * @param {String} property A field on your records
24887      * @param {Number} start The record index to start at (defaults to 0)
24888      * @param {Number} end The last record index to include (defaults to length - 1)
24889      * @return {Number} The sum
24890      */
24891     sum : function(property, start, end){
24892         var rs = this.data.items, v = 0;
24893         start = start || 0;
24894         end = (end || end === 0) ? end : rs.length-1;
24895
24896         for(var i = start; i <= end; i++){
24897             v += (rs[i].data[property] || 0);
24898         }
24899         return v;
24900     },
24901
24902     /**
24903      * Filter the records by a specified property.
24904      * @param {String} field A field on your records
24905      * @param {String/RegExp} value Either a string that the field
24906      * should start with or a RegExp to test against the field
24907      * @param {Boolean} anyMatch True to match any part not just the beginning
24908      */
24909     filter : function(property, value, anyMatch){
24910         var fn = this.createFilterFn(property, value, anyMatch);
24911         return fn ? this.filterBy(fn) : this.clearFilter();
24912     },
24913
24914     /**
24915      * Filter by a function. The specified function will be called with each
24916      * record in this data source. If the function returns true the record is included,
24917      * otherwise it is filtered.
24918      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24919      * @param {Object} scope (optional) The scope of the function (defaults to this)
24920      */
24921     filterBy : function(fn, scope){
24922         this.snapshot = this.snapshot || this.data;
24923         this.data = this.queryBy(fn, scope||this);
24924         this.fireEvent("datachanged", this);
24925     },
24926
24927     /**
24928      * Query the records by a specified property.
24929      * @param {String} field A field on your records
24930      * @param {String/RegExp} value Either a string that the field
24931      * should start with or a RegExp to test against the field
24932      * @param {Boolean} anyMatch True to match any part not just the beginning
24933      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24934      */
24935     query : function(property, value, anyMatch){
24936         var fn = this.createFilterFn(property, value, anyMatch);
24937         return fn ? this.queryBy(fn) : this.data.clone();
24938     },
24939
24940     /**
24941      * Query by a function. The specified function will be called with each
24942      * record in this data source. If the function returns true the record is included
24943      * in the results.
24944      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24945      * @param {Object} scope (optional) The scope of the function (defaults to this)
24946       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24947      **/
24948     queryBy : function(fn, scope){
24949         var data = this.snapshot || this.data;
24950         return data.filterBy(fn, scope||this);
24951     },
24952
24953     /**
24954      * Collects unique values for a particular dataIndex from this store.
24955      * @param {String} dataIndex The property to collect
24956      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24957      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24958      * @return {Array} An array of the unique values
24959      **/
24960     collect : function(dataIndex, allowNull, bypassFilter){
24961         var d = (bypassFilter === true && this.snapshot) ?
24962                 this.snapshot.items : this.data.items;
24963         var v, sv, r = [], l = {};
24964         for(var i = 0, len = d.length; i < len; i++){
24965             v = d[i].data[dataIndex];
24966             sv = String(v);
24967             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24968                 l[sv] = true;
24969                 r[r.length] = v;
24970             }
24971         }
24972         return r;
24973     },
24974
24975     /**
24976      * Revert to a view of the Record cache with no filtering applied.
24977      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24978      */
24979     clearFilter : function(suppressEvent){
24980         if(this.snapshot && this.snapshot != this.data){
24981             this.data = this.snapshot;
24982             delete this.snapshot;
24983             if(suppressEvent !== true){
24984                 this.fireEvent("datachanged", this);
24985             }
24986         }
24987     },
24988
24989     // private
24990     afterEdit : function(record){
24991         if(this.modified.indexOf(record) == -1){
24992             this.modified.push(record);
24993         }
24994         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24995     },
24996     
24997     // private
24998     afterReject : function(record){
24999         this.modified.remove(record);
25000         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25001     },
25002
25003     // private
25004     afterCommit : function(record){
25005         this.modified.remove(record);
25006         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25007     },
25008
25009     /**
25010      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25011      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25012      */
25013     commitChanges : function(){
25014         var m = this.modified.slice(0);
25015         this.modified = [];
25016         for(var i = 0, len = m.length; i < len; i++){
25017             m[i].commit();
25018         }
25019     },
25020
25021     /**
25022      * Cancel outstanding changes on all changed records.
25023      */
25024     rejectChanges : function(){
25025         var m = this.modified.slice(0);
25026         this.modified = [];
25027         for(var i = 0, len = m.length; i < len; i++){
25028             m[i].reject();
25029         }
25030     },
25031
25032     onMetaChange : function(meta, rtype, o){
25033         this.recordType = rtype;
25034         this.fields = rtype.prototype.fields;
25035         delete this.snapshot;
25036         this.sortInfo = meta.sortInfo || this.sortInfo;
25037         this.modified = [];
25038         this.fireEvent('metachange', this, this.reader.meta);
25039     },
25040     
25041     moveIndex : function(data, type)
25042     {
25043         var index = this.indexOf(data);
25044         
25045         var newIndex = index + type;
25046         
25047         this.remove(data);
25048         
25049         this.insert(newIndex, data);
25050         
25051     }
25052 });/*
25053  * Based on:
25054  * Ext JS Library 1.1.1
25055  * Copyright(c) 2006-2007, Ext JS, LLC.
25056  *
25057  * Originally Released Under LGPL - original licence link has changed is not relivant.
25058  *
25059  * Fork - LGPL
25060  * <script type="text/javascript">
25061  */
25062
25063 /**
25064  * @class Roo.data.SimpleStore
25065  * @extends Roo.data.Store
25066  * Small helper class to make creating Stores from Array data easier.
25067  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25068  * @cfg {Array} fields An array of field definition objects, or field name strings.
25069  * @cfg {Object} an existing reader (eg. copied from another store)
25070  * @cfg {Array} data The multi-dimensional array of data
25071  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25072  * @cfg {Roo.data.Reader} reader  [not-required] 
25073  * @constructor
25074  * @param {Object} config
25075  */
25076 Roo.data.SimpleStore = function(config)
25077 {
25078     Roo.data.SimpleStore.superclass.constructor.call(this, {
25079         isLocal : true,
25080         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25081                 id: config.id
25082             },
25083             Roo.data.Record.create(config.fields)
25084         ),
25085         proxy : new Roo.data.MemoryProxy(config.data)
25086     });
25087     this.load();
25088 };
25089 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25090  * Based on:
25091  * Ext JS Library 1.1.1
25092  * Copyright(c) 2006-2007, Ext JS, LLC.
25093  *
25094  * Originally Released Under LGPL - original licence link has changed is not relivant.
25095  *
25096  * Fork - LGPL
25097  * <script type="text/javascript">
25098  */
25099
25100 /**
25101 /**
25102  * @extends Roo.data.Store
25103  * @class Roo.data.JsonStore
25104  * Small helper class to make creating Stores for JSON data easier. <br/>
25105 <pre><code>
25106 var store = new Roo.data.JsonStore({
25107     url: 'get-images.php',
25108     root: 'images',
25109     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25110 });
25111 </code></pre>
25112  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25113  * JsonReader and HttpProxy (unless inline data is provided).</b>
25114  * @cfg {Array} fields An array of field definition objects, or field name strings.
25115  * @constructor
25116  * @param {Object} config
25117  */
25118 Roo.data.JsonStore = function(c){
25119     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25120         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25121         reader: new Roo.data.JsonReader(c, c.fields)
25122     }));
25123 };
25124 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25125  * Based on:
25126  * Ext JS Library 1.1.1
25127  * Copyright(c) 2006-2007, Ext JS, LLC.
25128  *
25129  * Originally Released Under LGPL - original licence link has changed is not relivant.
25130  *
25131  * Fork - LGPL
25132  * <script type="text/javascript">
25133  */
25134
25135  
25136 Roo.data.Field = function(config){
25137     if(typeof config == "string"){
25138         config = {name: config};
25139     }
25140     Roo.apply(this, config);
25141     
25142     if(!this.type){
25143         this.type = "auto";
25144     }
25145     
25146     var st = Roo.data.SortTypes;
25147     // named sortTypes are supported, here we look them up
25148     if(typeof this.sortType == "string"){
25149         this.sortType = st[this.sortType];
25150     }
25151     
25152     // set default sortType for strings and dates
25153     if(!this.sortType){
25154         switch(this.type){
25155             case "string":
25156                 this.sortType = st.asUCString;
25157                 break;
25158             case "date":
25159                 this.sortType = st.asDate;
25160                 break;
25161             default:
25162                 this.sortType = st.none;
25163         }
25164     }
25165
25166     // define once
25167     var stripRe = /[\$,%]/g;
25168
25169     // prebuilt conversion function for this field, instead of
25170     // switching every time we're reading a value
25171     if(!this.convert){
25172         var cv, dateFormat = this.dateFormat;
25173         switch(this.type){
25174             case "":
25175             case "auto":
25176             case undefined:
25177                 cv = function(v){ return v; };
25178                 break;
25179             case "string":
25180                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25181                 break;
25182             case "int":
25183                 cv = function(v){
25184                     return v !== undefined && v !== null && v !== '' ?
25185                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25186                     };
25187                 break;
25188             case "float":
25189                 cv = function(v){
25190                     return v !== undefined && v !== null && v !== '' ?
25191                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25192                     };
25193                 break;
25194             case "bool":
25195             case "boolean":
25196                 cv = function(v){ return v === true || v === "true" || v == 1; };
25197                 break;
25198             case "date":
25199                 cv = function(v){
25200                     if(!v){
25201                         return '';
25202                     }
25203                     if(v instanceof Date){
25204                         return v;
25205                     }
25206                     if(dateFormat){
25207                         if(dateFormat == "timestamp"){
25208                             return new Date(v*1000);
25209                         }
25210                         return Date.parseDate(v, dateFormat);
25211                     }
25212                     var parsed = Date.parse(v);
25213                     return parsed ? new Date(parsed) : null;
25214                 };
25215              break;
25216             
25217         }
25218         this.convert = cv;
25219     }
25220 };
25221
25222 Roo.data.Field.prototype = {
25223     dateFormat: null,
25224     defaultValue: "",
25225     mapping: null,
25226     sortType : null,
25227     sortDir : "ASC"
25228 };/*
25229  * Based on:
25230  * Ext JS Library 1.1.1
25231  * Copyright(c) 2006-2007, Ext JS, LLC.
25232  *
25233  * Originally Released Under LGPL - original licence link has changed is not relivant.
25234  *
25235  * Fork - LGPL
25236  * <script type="text/javascript">
25237  */
25238  
25239 // Base class for reading structured data from a data source.  This class is intended to be
25240 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25241
25242 /**
25243  * @class Roo.data.DataReader
25244  * @abstract
25245  * Base class for reading structured data from a data source.  This class is intended to be
25246  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25247  */
25248
25249 Roo.data.DataReader = function(meta, recordType){
25250     
25251     this.meta = meta;
25252     
25253     this.recordType = recordType instanceof Array ? 
25254         Roo.data.Record.create(recordType) : recordType;
25255 };
25256
25257 Roo.data.DataReader.prototype = {
25258     
25259     
25260     readerType : 'Data',
25261      /**
25262      * Create an empty record
25263      * @param {Object} data (optional) - overlay some values
25264      * @return {Roo.data.Record} record created.
25265      */
25266     newRow :  function(d) {
25267         var da =  {};
25268         this.recordType.prototype.fields.each(function(c) {
25269             switch( c.type) {
25270                 case 'int' : da[c.name] = 0; break;
25271                 case 'date' : da[c.name] = new Date(); break;
25272                 case 'float' : da[c.name] = 0.0; break;
25273                 case 'boolean' : da[c.name] = false; break;
25274                 default : da[c.name] = ""; break;
25275             }
25276             
25277         });
25278         return new this.recordType(Roo.apply(da, d));
25279     }
25280     
25281     
25282 };/*
25283  * Based on:
25284  * Ext JS Library 1.1.1
25285  * Copyright(c) 2006-2007, Ext JS, LLC.
25286  *
25287  * Originally Released Under LGPL - original licence link has changed is not relivant.
25288  *
25289  * Fork - LGPL
25290  * <script type="text/javascript">
25291  */
25292
25293 /**
25294  * @class Roo.data.DataProxy
25295  * @extends Roo.util.Observable
25296  * @abstract
25297  * This class is an abstract base class for implementations which provide retrieval of
25298  * unformatted data objects.<br>
25299  * <p>
25300  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25301  * (of the appropriate type which knows how to parse the data object) to provide a block of
25302  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25303  * <p>
25304  * Custom implementations must implement the load method as described in
25305  * {@link Roo.data.HttpProxy#load}.
25306  */
25307 Roo.data.DataProxy = function(){
25308     this.addEvents({
25309         /**
25310          * @event beforeload
25311          * Fires before a network request is made to retrieve a data object.
25312          * @param {Object} This DataProxy object.
25313          * @param {Object} params The params parameter to the load function.
25314          */
25315         beforeload : true,
25316         /**
25317          * @event load
25318          * Fires before the load method's callback is called.
25319          * @param {Object} This DataProxy object.
25320          * @param {Object} o The data object.
25321          * @param {Object} arg The callback argument object passed to the load function.
25322          */
25323         load : true,
25324         /**
25325          * @event loadexception
25326          * Fires if an Exception occurs during data retrieval.
25327          * @param {Object} This DataProxy object.
25328          * @param {Object} o The data object.
25329          * @param {Object} arg The callback argument object passed to the load function.
25330          * @param {Object} e The Exception.
25331          */
25332         loadexception : true
25333     });
25334     Roo.data.DataProxy.superclass.constructor.call(this);
25335 };
25336
25337 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25338
25339     /**
25340      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25341      */
25342 /*
25343  * Based on:
25344  * Ext JS Library 1.1.1
25345  * Copyright(c) 2006-2007, Ext JS, LLC.
25346  *
25347  * Originally Released Under LGPL - original licence link has changed is not relivant.
25348  *
25349  * Fork - LGPL
25350  * <script type="text/javascript">
25351  */
25352 /**
25353  * @class Roo.data.MemoryProxy
25354  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25355  * to the Reader when its load method is called.
25356  * @constructor
25357  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25358  */
25359 Roo.data.MemoryProxy = function(data){
25360     if (data.data) {
25361         data = data.data;
25362     }
25363     Roo.data.MemoryProxy.superclass.constructor.call(this);
25364     this.data = data;
25365 };
25366
25367 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25368     
25369     /**
25370      * Load data from the requested source (in this case an in-memory
25371      * data object passed to the constructor), read the data object into
25372      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25373      * process that block using the passed callback.
25374      * @param {Object} params This parameter is not used by the MemoryProxy class.
25375      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25376      * object into a block of Roo.data.Records.
25377      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25378      * The function must be passed <ul>
25379      * <li>The Record block object</li>
25380      * <li>The "arg" argument from the load function</li>
25381      * <li>A boolean success indicator</li>
25382      * </ul>
25383      * @param {Object} scope The scope in which to call the callback
25384      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25385      */
25386     load : function(params, reader, callback, scope, arg){
25387         params = params || {};
25388         var result;
25389         try {
25390             result = reader.readRecords(params.data ? params.data :this.data);
25391         }catch(e){
25392             this.fireEvent("loadexception", this, arg, null, e);
25393             callback.call(scope, null, arg, false);
25394             return;
25395         }
25396         callback.call(scope, result, arg, true);
25397     },
25398     
25399     // private
25400     update : function(params, records){
25401         
25402     }
25403 });/*
25404  * Based on:
25405  * Ext JS Library 1.1.1
25406  * Copyright(c) 2006-2007, Ext JS, LLC.
25407  *
25408  * Originally Released Under LGPL - original licence link has changed is not relivant.
25409  *
25410  * Fork - LGPL
25411  * <script type="text/javascript">
25412  */
25413 /**
25414  * @class Roo.data.HttpProxy
25415  * @extends Roo.data.DataProxy
25416  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25417  * configured to reference a certain URL.<br><br>
25418  * <p>
25419  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25420  * from which the running page was served.<br><br>
25421  * <p>
25422  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25423  * <p>
25424  * Be aware that to enable the browser to parse an XML document, the server must set
25425  * the Content-Type header in the HTTP response to "text/xml".
25426  * @constructor
25427  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25428  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25429  * will be used to make the request.
25430  */
25431 Roo.data.HttpProxy = function(conn){
25432     Roo.data.HttpProxy.superclass.constructor.call(this);
25433     // is conn a conn config or a real conn?
25434     this.conn = conn;
25435     this.useAjax = !conn || !conn.events;
25436   
25437 };
25438
25439 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25440     // thse are take from connection...
25441     
25442     /**
25443      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25444      */
25445     /**
25446      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25447      * extra parameters to each request made by this object. (defaults to undefined)
25448      */
25449     /**
25450      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25451      *  to each request made by this object. (defaults to undefined)
25452      */
25453     /**
25454      * @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)
25455      */
25456     /**
25457      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25458      */
25459      /**
25460      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25461      * @type Boolean
25462      */
25463   
25464
25465     /**
25466      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25467      * @type Boolean
25468      */
25469     /**
25470      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25471      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25472      * a finer-grained basis than the DataProxy events.
25473      */
25474     getConnection : function(){
25475         return this.useAjax ? Roo.Ajax : this.conn;
25476     },
25477
25478     /**
25479      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25480      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25481      * process that block using the passed callback.
25482      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25483      * for the request to the remote server.
25484      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25485      * object into a block of Roo.data.Records.
25486      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25487      * The function must be passed <ul>
25488      * <li>The Record block object</li>
25489      * <li>The "arg" argument from the load function</li>
25490      * <li>A boolean success indicator</li>
25491      * </ul>
25492      * @param {Object} scope The scope in which to call the callback
25493      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25494      */
25495     load : function(params, reader, callback, scope, arg){
25496         if(this.fireEvent("beforeload", this, params) !== false){
25497             var  o = {
25498                 params : params || {},
25499                 request: {
25500                     callback : callback,
25501                     scope : scope,
25502                     arg : arg
25503                 },
25504                 reader: reader,
25505                 callback : this.loadResponse,
25506                 scope: this
25507             };
25508             if(this.useAjax){
25509                 Roo.applyIf(o, this.conn);
25510                 if(this.activeRequest){
25511                     Roo.Ajax.abort(this.activeRequest);
25512                 }
25513                 this.activeRequest = Roo.Ajax.request(o);
25514             }else{
25515                 this.conn.request(o);
25516             }
25517         }else{
25518             callback.call(scope||this, null, arg, false);
25519         }
25520     },
25521
25522     // private
25523     loadResponse : function(o, success, response){
25524         delete this.activeRequest;
25525         if(!success){
25526             this.fireEvent("loadexception", this, o, response);
25527             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25528             return;
25529         }
25530         var result;
25531         try {
25532             result = o.reader.read(response);
25533         }catch(e){
25534             o.success = false;
25535             o.raw = { errorMsg : response.responseText };
25536             this.fireEvent("loadexception", this, o, response, e);
25537             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25538             return;
25539         }
25540         
25541         this.fireEvent("load", this, o, o.request.arg);
25542         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25543     },
25544
25545     // private
25546     update : function(dataSet){
25547
25548     },
25549
25550     // private
25551     updateResponse : function(dataSet){
25552
25553     }
25554 });/*
25555  * Based on:
25556  * Ext JS Library 1.1.1
25557  * Copyright(c) 2006-2007, Ext JS, LLC.
25558  *
25559  * Originally Released Under LGPL - original licence link has changed is not relivant.
25560  *
25561  * Fork - LGPL
25562  * <script type="text/javascript">
25563  */
25564
25565 /**
25566  * @class Roo.data.ScriptTagProxy
25567  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25568  * other than the originating domain of the running page.<br><br>
25569  * <p>
25570  * <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
25571  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25572  * <p>
25573  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25574  * source code that is used as the source inside a &lt;script> tag.<br><br>
25575  * <p>
25576  * In order for the browser to process the returned data, the server must wrap the data object
25577  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25578  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25579  * depending on whether the callback name was passed:
25580  * <p>
25581  * <pre><code>
25582 boolean scriptTag = false;
25583 String cb = request.getParameter("callback");
25584 if (cb != null) {
25585     scriptTag = true;
25586     response.setContentType("text/javascript");
25587 } else {
25588     response.setContentType("application/x-json");
25589 }
25590 Writer out = response.getWriter();
25591 if (scriptTag) {
25592     out.write(cb + "(");
25593 }
25594 out.print(dataBlock.toJsonString());
25595 if (scriptTag) {
25596     out.write(");");
25597 }
25598 </pre></code>
25599  *
25600  * @constructor
25601  * @param {Object} config A configuration object.
25602  */
25603 Roo.data.ScriptTagProxy = function(config){
25604     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25605     Roo.apply(this, config);
25606     this.head = document.getElementsByTagName("head")[0];
25607 };
25608
25609 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25610
25611 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25612     /**
25613      * @cfg {String} url The URL from which to request the data object.
25614      */
25615     /**
25616      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25617      */
25618     timeout : 30000,
25619     /**
25620      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25621      * the server the name of the callback function set up by the load call to process the returned data object.
25622      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25623      * javascript output which calls this named function passing the data object as its only parameter.
25624      */
25625     callbackParam : "callback",
25626     /**
25627      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25628      * name to the request.
25629      */
25630     nocache : true,
25631
25632     /**
25633      * Load data from the configured URL, read the data object into
25634      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25635      * process that block using the passed callback.
25636      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25637      * for the request to the remote server.
25638      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25639      * object into a block of Roo.data.Records.
25640      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25641      * The function must be passed <ul>
25642      * <li>The Record block object</li>
25643      * <li>The "arg" argument from the load function</li>
25644      * <li>A boolean success indicator</li>
25645      * </ul>
25646      * @param {Object} scope The scope in which to call the callback
25647      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25648      */
25649     load : function(params, reader, callback, scope, arg){
25650         if(this.fireEvent("beforeload", this, params) !== false){
25651
25652             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25653
25654             var url = this.url;
25655             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25656             if(this.nocache){
25657                 url += "&_dc=" + (new Date().getTime());
25658             }
25659             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25660             var trans = {
25661                 id : transId,
25662                 cb : "stcCallback"+transId,
25663                 scriptId : "stcScript"+transId,
25664                 params : params,
25665                 arg : arg,
25666                 url : url,
25667                 callback : callback,
25668                 scope : scope,
25669                 reader : reader
25670             };
25671             var conn = this;
25672
25673             window[trans.cb] = function(o){
25674                 conn.handleResponse(o, trans);
25675             };
25676
25677             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25678
25679             if(this.autoAbort !== false){
25680                 this.abort();
25681             }
25682
25683             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25684
25685             var script = document.createElement("script");
25686             script.setAttribute("src", url);
25687             script.setAttribute("type", "text/javascript");
25688             script.setAttribute("id", trans.scriptId);
25689             this.head.appendChild(script);
25690
25691             this.trans = trans;
25692         }else{
25693             callback.call(scope||this, null, arg, false);
25694         }
25695     },
25696
25697     // private
25698     isLoading : function(){
25699         return this.trans ? true : false;
25700     },
25701
25702     /**
25703      * Abort the current server request.
25704      */
25705     abort : function(){
25706         if(this.isLoading()){
25707             this.destroyTrans(this.trans);
25708         }
25709     },
25710
25711     // private
25712     destroyTrans : function(trans, isLoaded){
25713         this.head.removeChild(document.getElementById(trans.scriptId));
25714         clearTimeout(trans.timeoutId);
25715         if(isLoaded){
25716             window[trans.cb] = undefined;
25717             try{
25718                 delete window[trans.cb];
25719             }catch(e){}
25720         }else{
25721             // if hasn't been loaded, wait for load to remove it to prevent script error
25722             window[trans.cb] = function(){
25723                 window[trans.cb] = undefined;
25724                 try{
25725                     delete window[trans.cb];
25726                 }catch(e){}
25727             };
25728         }
25729     },
25730
25731     // private
25732     handleResponse : function(o, trans){
25733         this.trans = false;
25734         this.destroyTrans(trans, true);
25735         var result;
25736         try {
25737             result = trans.reader.readRecords(o);
25738         }catch(e){
25739             this.fireEvent("loadexception", this, o, trans.arg, e);
25740             trans.callback.call(trans.scope||window, null, trans.arg, false);
25741             return;
25742         }
25743         this.fireEvent("load", this, o, trans.arg);
25744         trans.callback.call(trans.scope||window, result, trans.arg, true);
25745     },
25746
25747     // private
25748     handleFailure : function(trans){
25749         this.trans = false;
25750         this.destroyTrans(trans, false);
25751         this.fireEvent("loadexception", this, null, trans.arg);
25752         trans.callback.call(trans.scope||window, null, trans.arg, false);
25753     }
25754 });/*
25755  * Based on:
25756  * Ext JS Library 1.1.1
25757  * Copyright(c) 2006-2007, Ext JS, LLC.
25758  *
25759  * Originally Released Under LGPL - original licence link has changed is not relivant.
25760  *
25761  * Fork - LGPL
25762  * <script type="text/javascript">
25763  */
25764
25765 /**
25766  * @class Roo.data.JsonReader
25767  * @extends Roo.data.DataReader
25768  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25769  * based on mappings in a provided Roo.data.Record constructor.
25770  * 
25771  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25772  * in the reply previously. 
25773  * 
25774  * <p>
25775  * Example code:
25776  * <pre><code>
25777 var RecordDef = Roo.data.Record.create([
25778     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25779     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25780 ]);
25781 var myReader = new Roo.data.JsonReader({
25782     totalProperty: "results",    // The property which contains the total dataset size (optional)
25783     root: "rows",                // The property which contains an Array of row objects
25784     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25785 }, RecordDef);
25786 </code></pre>
25787  * <p>
25788  * This would consume a JSON file like this:
25789  * <pre><code>
25790 { 'results': 2, 'rows': [
25791     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25792     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25793 }
25794 </code></pre>
25795  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25796  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25797  * paged from the remote server.
25798  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25799  * @cfg {String} root name of the property which contains the Array of row objects.
25800  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25801  * @cfg {Array} fields Array of field definition objects
25802  * @constructor
25803  * Create a new JsonReader
25804  * @param {Object} meta Metadata configuration options
25805  * @param {Object} recordType Either an Array of field definition objects,
25806  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25807  */
25808 Roo.data.JsonReader = function(meta, recordType){
25809     
25810     meta = meta || {};
25811     // set some defaults:
25812     Roo.applyIf(meta, {
25813         totalProperty: 'total',
25814         successProperty : 'success',
25815         root : 'data',
25816         id : 'id'
25817     });
25818     
25819     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25820 };
25821 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25822     
25823     readerType : 'Json',
25824     
25825     /**
25826      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25827      * Used by Store query builder to append _requestMeta to params.
25828      * 
25829      */
25830     metaFromRemote : false,
25831     /**
25832      * This method is only used by a DataProxy which has retrieved data from a remote server.
25833      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25834      * @return {Object} data A data block which is used by an Roo.data.Store object as
25835      * a cache of Roo.data.Records.
25836      */
25837     read : function(response){
25838         var json = response.responseText;
25839        
25840         var o = /* eval:var:o */ eval("("+json+")");
25841         if(!o) {
25842             throw {message: "JsonReader.read: Json object not found"};
25843         }
25844         
25845         if(o.metaData){
25846             
25847             delete this.ef;
25848             this.metaFromRemote = true;
25849             this.meta = o.metaData;
25850             this.recordType = Roo.data.Record.create(o.metaData.fields);
25851             this.onMetaChange(this.meta, this.recordType, o);
25852         }
25853         return this.readRecords(o);
25854     },
25855
25856     // private function a store will implement
25857     onMetaChange : function(meta, recordType, o){
25858
25859     },
25860
25861     /**
25862          * @ignore
25863          */
25864     simpleAccess: function(obj, subsc) {
25865         return obj[subsc];
25866     },
25867
25868         /**
25869          * @ignore
25870          */
25871     getJsonAccessor: function(){
25872         var re = /[\[\.]/;
25873         return function(expr) {
25874             try {
25875                 return(re.test(expr))
25876                     ? new Function("obj", "return obj." + expr)
25877                     : function(obj){
25878                         return obj[expr];
25879                     };
25880             } catch(e){}
25881             return Roo.emptyFn;
25882         };
25883     }(),
25884
25885     /**
25886      * Create a data block containing Roo.data.Records from an XML document.
25887      * @param {Object} o An object which contains an Array of row objects in the property specified
25888      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25889      * which contains the total size of the dataset.
25890      * @return {Object} data A data block which is used by an Roo.data.Store object as
25891      * a cache of Roo.data.Records.
25892      */
25893     readRecords : function(o){
25894         /**
25895          * After any data loads, the raw JSON data is available for further custom processing.
25896          * @type Object
25897          */
25898         this.o = o;
25899         var s = this.meta, Record = this.recordType,
25900             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25901
25902 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25903         if (!this.ef) {
25904             if(s.totalProperty) {
25905                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25906                 }
25907                 if(s.successProperty) {
25908                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25909                 }
25910                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25911                 if (s.id) {
25912                         var g = this.getJsonAccessor(s.id);
25913                         this.getId = function(rec) {
25914                                 var r = g(rec);  
25915                                 return (r === undefined || r === "") ? null : r;
25916                         };
25917                 } else {
25918                         this.getId = function(){return null;};
25919                 }
25920             this.ef = [];
25921             for(var jj = 0; jj < fl; jj++){
25922                 f = fi[jj];
25923                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25924                 this.ef[jj] = this.getJsonAccessor(map);
25925             }
25926         }
25927
25928         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25929         if(s.totalProperty){
25930             var vt = parseInt(this.getTotal(o), 10);
25931             if(!isNaN(vt)){
25932                 totalRecords = vt;
25933             }
25934         }
25935         if(s.successProperty){
25936             var vs = this.getSuccess(o);
25937             if(vs === false || vs === 'false'){
25938                 success = false;
25939             }
25940         }
25941         var records = [];
25942         for(var i = 0; i < c; i++){
25943             var n = root[i];
25944             var values = {};
25945             var id = this.getId(n);
25946             for(var j = 0; j < fl; j++){
25947                 f = fi[j];
25948                                 var v = this.ef[j](n);
25949                                 if (!f.convert) {
25950                                         Roo.log('missing convert for ' + f.name);
25951                                         Roo.log(f);
25952                                         continue;
25953                                 }
25954                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25955             }
25956                         if (!Record) {
25957                                 return {
25958                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25959                                         success : false,
25960                                         records : [],
25961                                         totalRecords : 0
25962                                 };
25963                         }
25964             var record = new Record(values, id);
25965             record.json = n;
25966             records[i] = record;
25967         }
25968         return {
25969             raw : o,
25970             success : success,
25971             records : records,
25972             totalRecords : totalRecords
25973         };
25974     },
25975     // used when loading children.. @see loadDataFromChildren
25976     toLoadData: function(rec)
25977     {
25978         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25979         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25980         return { data : data, total : data.length };
25981         
25982     }
25983 });/*
25984  * Based on:
25985  * Ext JS Library 1.1.1
25986  * Copyright(c) 2006-2007, Ext JS, LLC.
25987  *
25988  * Originally Released Under LGPL - original licence link has changed is not relivant.
25989  *
25990  * Fork - LGPL
25991  * <script type="text/javascript">
25992  */
25993
25994 /**
25995  * @class Roo.data.XmlReader
25996  * @extends Roo.data.DataReader
25997  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25998  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25999  * <p>
26000  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26001  * header in the HTTP response must be set to "text/xml".</em>
26002  * <p>
26003  * Example code:
26004  * <pre><code>
26005 var RecordDef = Roo.data.Record.create([
26006    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26007    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26008 ]);
26009 var myReader = new Roo.data.XmlReader({
26010    totalRecords: "results", // The element which contains the total dataset size (optional)
26011    record: "row",           // The repeated element which contains row information
26012    id: "id"                 // The element within the row that provides an ID for the record (optional)
26013 }, RecordDef);
26014 </code></pre>
26015  * <p>
26016  * This would consume an XML file like this:
26017  * <pre><code>
26018 &lt;?xml?>
26019 &lt;dataset>
26020  &lt;results>2&lt;/results>
26021  &lt;row>
26022    &lt;id>1&lt;/id>
26023    &lt;name>Bill&lt;/name>
26024    &lt;occupation>Gardener&lt;/occupation>
26025  &lt;/row>
26026  &lt;row>
26027    &lt;id>2&lt;/id>
26028    &lt;name>Ben&lt;/name>
26029    &lt;occupation>Horticulturalist&lt;/occupation>
26030  &lt;/row>
26031 &lt;/dataset>
26032 </code></pre>
26033  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26034  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26035  * paged from the remote server.
26036  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26037  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26038  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26039  * a record identifier value.
26040  * @constructor
26041  * Create a new XmlReader
26042  * @param {Object} meta Metadata configuration options
26043  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26044  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26045  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26046  */
26047 Roo.data.XmlReader = function(meta, recordType){
26048     meta = meta || {};
26049     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26050 };
26051 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26052     
26053     readerType : 'Xml',
26054     
26055     /**
26056      * This method is only used by a DataProxy which has retrieved data from a remote server.
26057          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26058          * to contain a method called 'responseXML' that returns an XML document object.
26059      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26060      * a cache of Roo.data.Records.
26061      */
26062     read : function(response){
26063         var doc = response.responseXML;
26064         if(!doc) {
26065             throw {message: "XmlReader.read: XML Document not available"};
26066         }
26067         return this.readRecords(doc);
26068     },
26069
26070     /**
26071      * Create a data block containing Roo.data.Records from an XML document.
26072          * @param {Object} doc A parsed XML document.
26073      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26074      * a cache of Roo.data.Records.
26075      */
26076     readRecords : function(doc){
26077         /**
26078          * After any data loads/reads, the raw XML Document is available for further custom processing.
26079          * @type XMLDocument
26080          */
26081         this.xmlData = doc;
26082         var root = doc.documentElement || doc;
26083         var q = Roo.DomQuery;
26084         var recordType = this.recordType, fields = recordType.prototype.fields;
26085         var sid = this.meta.id;
26086         var totalRecords = 0, success = true;
26087         if(this.meta.totalRecords){
26088             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26089         }
26090         
26091         if(this.meta.success){
26092             var sv = q.selectValue(this.meta.success, root, true);
26093             success = sv !== false && sv !== 'false';
26094         }
26095         var records = [];
26096         var ns = q.select(this.meta.record, root);
26097         for(var i = 0, len = ns.length; i < len; i++) {
26098                 var n = ns[i];
26099                 var values = {};
26100                 var id = sid ? q.selectValue(sid, n) : undefined;
26101                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26102                     var f = fields.items[j];
26103                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26104                     v = f.convert(v);
26105                     values[f.name] = v;
26106                 }
26107                 var record = new recordType(values, id);
26108                 record.node = n;
26109                 records[records.length] = record;
26110             }
26111
26112             return {
26113                 success : success,
26114                 records : records,
26115                 totalRecords : totalRecords || records.length
26116             };
26117     }
26118 });/*
26119  * Based on:
26120  * Ext JS Library 1.1.1
26121  * Copyright(c) 2006-2007, Ext JS, LLC.
26122  *
26123  * Originally Released Under LGPL - original licence link has changed is not relivant.
26124  *
26125  * Fork - LGPL
26126  * <script type="text/javascript">
26127  */
26128
26129 /**
26130  * @class Roo.data.ArrayReader
26131  * @extends Roo.data.DataReader
26132  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26133  * Each element of that Array represents a row of data fields. The
26134  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26135  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26136  * <p>
26137  * Example code:.
26138  * <pre><code>
26139 var RecordDef = Roo.data.Record.create([
26140     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26141     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26142 ]);
26143 var myReader = new Roo.data.ArrayReader({
26144     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26145 }, RecordDef);
26146 </code></pre>
26147  * <p>
26148  * This would consume an Array like this:
26149  * <pre><code>
26150 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26151   </code></pre>
26152  
26153  * @constructor
26154  * Create a new JsonReader
26155  * @param {Object} meta Metadata configuration options.
26156  * @param {Object|Array} recordType Either an Array of field definition objects
26157  * 
26158  * @cfg {Array} fields Array of field definition objects
26159  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26160  * as specified to {@link Roo.data.Record#create},
26161  * or an {@link Roo.data.Record} object
26162  *
26163  * 
26164  * created using {@link Roo.data.Record#create}.
26165  */
26166 Roo.data.ArrayReader = function(meta, recordType)
26167 {    
26168     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26169 };
26170
26171 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26172     
26173       /**
26174      * Create a data block containing Roo.data.Records from an XML document.
26175      * @param {Object} o An Array of row objects which represents the dataset.
26176      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26177      * a cache of Roo.data.Records.
26178      */
26179     readRecords : function(o)
26180     {
26181         var sid = this.meta ? this.meta.id : null;
26182         var recordType = this.recordType, fields = recordType.prototype.fields;
26183         var records = [];
26184         var root = o;
26185         for(var i = 0; i < root.length; i++){
26186             var n = root[i];
26187             var values = {};
26188             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26189             for(var j = 0, jlen = fields.length; j < jlen; j++){
26190                 var f = fields.items[j];
26191                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26192                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26193                 v = f.convert(v);
26194                 values[f.name] = v;
26195             }
26196             var record = new recordType(values, id);
26197             record.json = n;
26198             records[records.length] = record;
26199         }
26200         return {
26201             records : records,
26202             totalRecords : records.length
26203         };
26204     },
26205     // used when loading children.. @see loadDataFromChildren
26206     toLoadData: function(rec)
26207     {
26208         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26209         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26210         
26211     }
26212     
26213     
26214 });/*
26215  * Based on:
26216  * Ext JS Library 1.1.1
26217  * Copyright(c) 2006-2007, Ext JS, LLC.
26218  *
26219  * Originally Released Under LGPL - original licence link has changed is not relivant.
26220  *
26221  * Fork - LGPL
26222  * <script type="text/javascript">
26223  */
26224
26225
26226 /**
26227  * @class Roo.data.Tree
26228  * @extends Roo.util.Observable
26229  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26230  * in the tree have most standard DOM functionality.
26231  * @constructor
26232  * @param {Node} root (optional) The root node
26233  */
26234 Roo.data.Tree = function(root){
26235    this.nodeHash = {};
26236    /**
26237     * The root node for this tree
26238     * @type Node
26239     */
26240    this.root = null;
26241    if(root){
26242        this.setRootNode(root);
26243    }
26244    this.addEvents({
26245        /**
26246         * @event append
26247         * Fires when a new child node is appended to a node in this tree.
26248         * @param {Tree} tree The owner tree
26249         * @param {Node} parent The parent node
26250         * @param {Node} node The newly appended node
26251         * @param {Number} index The index of the newly appended node
26252         */
26253        "append" : true,
26254        /**
26255         * @event remove
26256         * Fires when a child node is removed from a node in this tree.
26257         * @param {Tree} tree The owner tree
26258         * @param {Node} parent The parent node
26259         * @param {Node} node The child node removed
26260         */
26261        "remove" : true,
26262        /**
26263         * @event move
26264         * Fires when a node is moved to a new location in the tree
26265         * @param {Tree} tree The owner tree
26266         * @param {Node} node The node moved
26267         * @param {Node} oldParent The old parent of this node
26268         * @param {Node} newParent The new parent of this node
26269         * @param {Number} index The index it was moved to
26270         */
26271        "move" : true,
26272        /**
26273         * @event insert
26274         * Fires when a new child node is inserted in a node in this tree.
26275         * @param {Tree} tree The owner tree
26276         * @param {Node} parent The parent node
26277         * @param {Node} node The child node inserted
26278         * @param {Node} refNode The child node the node was inserted before
26279         */
26280        "insert" : true,
26281        /**
26282         * @event beforeappend
26283         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26284         * @param {Tree} tree The owner tree
26285         * @param {Node} parent The parent node
26286         * @param {Node} node The child node to be appended
26287         */
26288        "beforeappend" : true,
26289        /**
26290         * @event beforeremove
26291         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26292         * @param {Tree} tree The owner tree
26293         * @param {Node} parent The parent node
26294         * @param {Node} node The child node to be removed
26295         */
26296        "beforeremove" : true,
26297        /**
26298         * @event beforemove
26299         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26300         * @param {Tree} tree The owner tree
26301         * @param {Node} node The node being moved
26302         * @param {Node} oldParent The parent of the node
26303         * @param {Node} newParent The new parent the node is moving to
26304         * @param {Number} index The index it is being moved to
26305         */
26306        "beforemove" : true,
26307        /**
26308         * @event beforeinsert
26309         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26310         * @param {Tree} tree The owner tree
26311         * @param {Node} parent The parent node
26312         * @param {Node} node The child node to be inserted
26313         * @param {Node} refNode The child node the node is being inserted before
26314         */
26315        "beforeinsert" : true
26316    });
26317
26318     Roo.data.Tree.superclass.constructor.call(this);
26319 };
26320
26321 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26322     pathSeparator: "/",
26323
26324     proxyNodeEvent : function(){
26325         return this.fireEvent.apply(this, arguments);
26326     },
26327
26328     /**
26329      * Returns the root node for this tree.
26330      * @return {Node}
26331      */
26332     getRootNode : function(){
26333         return this.root;
26334     },
26335
26336     /**
26337      * Sets the root node for this tree.
26338      * @param {Node} node
26339      * @return {Node}
26340      */
26341     setRootNode : function(node){
26342         this.root = node;
26343         node.ownerTree = this;
26344         node.isRoot = true;
26345         this.registerNode(node);
26346         return node;
26347     },
26348
26349     /**
26350      * Gets a node in this tree by its id.
26351      * @param {String} id
26352      * @return {Node}
26353      */
26354     getNodeById : function(id){
26355         return this.nodeHash[id];
26356     },
26357
26358     registerNode : function(node){
26359         this.nodeHash[node.id] = node;
26360     },
26361
26362     unregisterNode : function(node){
26363         delete this.nodeHash[node.id];
26364     },
26365
26366     toString : function(){
26367         return "[Tree"+(this.id?" "+this.id:"")+"]";
26368     }
26369 });
26370
26371 /**
26372  * @class Roo.data.Node
26373  * @extends Roo.util.Observable
26374  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26375  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26376  * @constructor
26377  * @param {Object} attributes The attributes/config for the node
26378  */
26379 Roo.data.Node = function(attributes){
26380     /**
26381      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26382      * @type {Object}
26383      */
26384     this.attributes = attributes || {};
26385     this.leaf = this.attributes.leaf;
26386     /**
26387      * The node id. @type String
26388      */
26389     this.id = this.attributes.id;
26390     if(!this.id){
26391         this.id = Roo.id(null, "ynode-");
26392         this.attributes.id = this.id;
26393     }
26394      
26395     
26396     /**
26397      * All child nodes of this node. @type Array
26398      */
26399     this.childNodes = [];
26400     if(!this.childNodes.indexOf){ // indexOf is a must
26401         this.childNodes.indexOf = function(o){
26402             for(var i = 0, len = this.length; i < len; i++){
26403                 if(this[i] == o) {
26404                     return i;
26405                 }
26406             }
26407             return -1;
26408         };
26409     }
26410     /**
26411      * The parent node for this node. @type Node
26412      */
26413     this.parentNode = null;
26414     /**
26415      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26416      */
26417     this.firstChild = null;
26418     /**
26419      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26420      */
26421     this.lastChild = null;
26422     /**
26423      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26424      */
26425     this.previousSibling = null;
26426     /**
26427      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26428      */
26429     this.nextSibling = null;
26430
26431     this.addEvents({
26432        /**
26433         * @event append
26434         * Fires when a new child node is appended
26435         * @param {Tree} tree The owner tree
26436         * @param {Node} this This node
26437         * @param {Node} node The newly appended node
26438         * @param {Number} index The index of the newly appended node
26439         */
26440        "append" : true,
26441        /**
26442         * @event remove
26443         * Fires when a child node is removed
26444         * @param {Tree} tree The owner tree
26445         * @param {Node} this This node
26446         * @param {Node} node The removed node
26447         */
26448        "remove" : true,
26449        /**
26450         * @event move
26451         * Fires when this node is moved to a new location in the tree
26452         * @param {Tree} tree The owner tree
26453         * @param {Node} this This node
26454         * @param {Node} oldParent The old parent of this node
26455         * @param {Node} newParent The new parent of this node
26456         * @param {Number} index The index it was moved to
26457         */
26458        "move" : true,
26459        /**
26460         * @event insert
26461         * Fires when a new child node is inserted.
26462         * @param {Tree} tree The owner tree
26463         * @param {Node} this This node
26464         * @param {Node} node The child node inserted
26465         * @param {Node} refNode The child node the node was inserted before
26466         */
26467        "insert" : true,
26468        /**
26469         * @event beforeappend
26470         * Fires before a new child is appended, return false to cancel the append.
26471         * @param {Tree} tree The owner tree
26472         * @param {Node} this This node
26473         * @param {Node} node The child node to be appended
26474         */
26475        "beforeappend" : true,
26476        /**
26477         * @event beforeremove
26478         * Fires before a child is removed, return false to cancel the remove.
26479         * @param {Tree} tree The owner tree
26480         * @param {Node} this This node
26481         * @param {Node} node The child node to be removed
26482         */
26483        "beforeremove" : true,
26484        /**
26485         * @event beforemove
26486         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26487         * @param {Tree} tree The owner tree
26488         * @param {Node} this This node
26489         * @param {Node} oldParent The parent of this node
26490         * @param {Node} newParent The new parent this node is moving to
26491         * @param {Number} index The index it is being moved to
26492         */
26493        "beforemove" : true,
26494        /**
26495         * @event beforeinsert
26496         * Fires before a new child is inserted, return false to cancel the insert.
26497         * @param {Tree} tree The owner tree
26498         * @param {Node} this This node
26499         * @param {Node} node The child node to be inserted
26500         * @param {Node} refNode The child node the node is being inserted before
26501         */
26502        "beforeinsert" : true
26503    });
26504     this.listeners = this.attributes.listeners;
26505     Roo.data.Node.superclass.constructor.call(this);
26506 };
26507
26508 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26509     fireEvent : function(evtName){
26510         // first do standard event for this node
26511         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26512             return false;
26513         }
26514         // then bubble it up to the tree if the event wasn't cancelled
26515         var ot = this.getOwnerTree();
26516         if(ot){
26517             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26518                 return false;
26519             }
26520         }
26521         return true;
26522     },
26523
26524     /**
26525      * Returns true if this node is a leaf
26526      * @return {Boolean}
26527      */
26528     isLeaf : function(){
26529         return this.leaf === true;
26530     },
26531
26532     // private
26533     setFirstChild : function(node){
26534         this.firstChild = node;
26535     },
26536
26537     //private
26538     setLastChild : function(node){
26539         this.lastChild = node;
26540     },
26541
26542
26543     /**
26544      * Returns true if this node is the last child of its parent
26545      * @return {Boolean}
26546      */
26547     isLast : function(){
26548        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26549     },
26550
26551     /**
26552      * Returns true if this node is the first child of its parent
26553      * @return {Boolean}
26554      */
26555     isFirst : function(){
26556        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26557     },
26558
26559     hasChildNodes : function(){
26560         return !this.isLeaf() && this.childNodes.length > 0;
26561     },
26562
26563     /**
26564      * Insert node(s) as the last child node of this node.
26565      * @param {Node/Array} node The node or Array of nodes to append
26566      * @return {Node} The appended node if single append, or null if an array was passed
26567      */
26568     appendChild : function(node){
26569         var multi = false;
26570         if(node instanceof Array){
26571             multi = node;
26572         }else if(arguments.length > 1){
26573             multi = arguments;
26574         }
26575         
26576         // if passed an array or multiple args do them one by one
26577         if(multi){
26578             for(var i = 0, len = multi.length; i < len; i++) {
26579                 this.appendChild(multi[i]);
26580             }
26581         }else{
26582             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26583                 return false;
26584             }
26585             var index = this.childNodes.length;
26586             var oldParent = node.parentNode;
26587             // it's a move, make sure we move it cleanly
26588             if(oldParent){
26589                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26590                     return false;
26591                 }
26592                 oldParent.removeChild(node);
26593             }
26594             
26595             index = this.childNodes.length;
26596             if(index == 0){
26597                 this.setFirstChild(node);
26598             }
26599             this.childNodes.push(node);
26600             node.parentNode = this;
26601             var ps = this.childNodes[index-1];
26602             if(ps){
26603                 node.previousSibling = ps;
26604                 ps.nextSibling = node;
26605             }else{
26606                 node.previousSibling = null;
26607             }
26608             node.nextSibling = null;
26609             this.setLastChild(node);
26610             node.setOwnerTree(this.getOwnerTree());
26611             this.fireEvent("append", this.ownerTree, this, node, index);
26612             if(this.ownerTree) {
26613                 this.ownerTree.fireEvent("appendnode", this, node, index);
26614             }
26615             if(oldParent){
26616                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26617             }
26618             return node;
26619         }
26620     },
26621
26622     /**
26623      * Removes a child node from this node.
26624      * @param {Node} node The node to remove
26625      * @return {Node} The removed node
26626      */
26627     removeChild : function(node){
26628         var index = this.childNodes.indexOf(node);
26629         if(index == -1){
26630             return false;
26631         }
26632         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26633             return false;
26634         }
26635
26636         // remove it from childNodes collection
26637         this.childNodes.splice(index, 1);
26638
26639         // update siblings
26640         if(node.previousSibling){
26641             node.previousSibling.nextSibling = node.nextSibling;
26642         }
26643         if(node.nextSibling){
26644             node.nextSibling.previousSibling = node.previousSibling;
26645         }
26646
26647         // update child refs
26648         if(this.firstChild == node){
26649             this.setFirstChild(node.nextSibling);
26650         }
26651         if(this.lastChild == node){
26652             this.setLastChild(node.previousSibling);
26653         }
26654
26655         node.setOwnerTree(null);
26656         // clear any references from the node
26657         node.parentNode = null;
26658         node.previousSibling = null;
26659         node.nextSibling = null;
26660         this.fireEvent("remove", this.ownerTree, this, node);
26661         return node;
26662     },
26663
26664     /**
26665      * Inserts the first node before the second node in this nodes childNodes collection.
26666      * @param {Node} node The node to insert
26667      * @param {Node} refNode The node to insert before (if null the node is appended)
26668      * @return {Node} The inserted node
26669      */
26670     insertBefore : function(node, refNode){
26671         if(!refNode){ // like standard Dom, refNode can be null for append
26672             return this.appendChild(node);
26673         }
26674         // nothing to do
26675         if(node == refNode){
26676             return false;
26677         }
26678
26679         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26680             return false;
26681         }
26682         var index = this.childNodes.indexOf(refNode);
26683         var oldParent = node.parentNode;
26684         var refIndex = index;
26685
26686         // when moving internally, indexes will change after remove
26687         if(oldParent == this && this.childNodes.indexOf(node) < index){
26688             refIndex--;
26689         }
26690
26691         // it's a move, make sure we move it cleanly
26692         if(oldParent){
26693             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26694                 return false;
26695             }
26696             oldParent.removeChild(node);
26697         }
26698         if(refIndex == 0){
26699             this.setFirstChild(node);
26700         }
26701         this.childNodes.splice(refIndex, 0, node);
26702         node.parentNode = this;
26703         var ps = this.childNodes[refIndex-1];
26704         if(ps){
26705             node.previousSibling = ps;
26706             ps.nextSibling = node;
26707         }else{
26708             node.previousSibling = null;
26709         }
26710         node.nextSibling = refNode;
26711         refNode.previousSibling = node;
26712         node.setOwnerTree(this.getOwnerTree());
26713         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26714         if(oldParent){
26715             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26716         }
26717         return node;
26718     },
26719
26720     /**
26721      * Returns the child node at the specified index.
26722      * @param {Number} index
26723      * @return {Node}
26724      */
26725     item : function(index){
26726         return this.childNodes[index];
26727     },
26728
26729     /**
26730      * Replaces one child node in this node with another.
26731      * @param {Node} newChild The replacement node
26732      * @param {Node} oldChild The node to replace
26733      * @return {Node} The replaced node
26734      */
26735     replaceChild : function(newChild, oldChild){
26736         this.insertBefore(newChild, oldChild);
26737         this.removeChild(oldChild);
26738         return oldChild;
26739     },
26740
26741     /**
26742      * Returns the index of a child node
26743      * @param {Node} node
26744      * @return {Number} The index of the node or -1 if it was not found
26745      */
26746     indexOf : function(child){
26747         return this.childNodes.indexOf(child);
26748     },
26749
26750     /**
26751      * Returns the tree this node is in.
26752      * @return {Tree}
26753      */
26754     getOwnerTree : function(){
26755         // if it doesn't have one, look for one
26756         if(!this.ownerTree){
26757             var p = this;
26758             while(p){
26759                 if(p.ownerTree){
26760                     this.ownerTree = p.ownerTree;
26761                     break;
26762                 }
26763                 p = p.parentNode;
26764             }
26765         }
26766         return this.ownerTree;
26767     },
26768
26769     /**
26770      * Returns depth of this node (the root node has a depth of 0)
26771      * @return {Number}
26772      */
26773     getDepth : function(){
26774         var depth = 0;
26775         var p = this;
26776         while(p.parentNode){
26777             ++depth;
26778             p = p.parentNode;
26779         }
26780         return depth;
26781     },
26782
26783     // private
26784     setOwnerTree : function(tree){
26785         // if it's move, we need to update everyone
26786         if(tree != this.ownerTree){
26787             if(this.ownerTree){
26788                 this.ownerTree.unregisterNode(this);
26789             }
26790             this.ownerTree = tree;
26791             var cs = this.childNodes;
26792             for(var i = 0, len = cs.length; i < len; i++) {
26793                 cs[i].setOwnerTree(tree);
26794             }
26795             if(tree){
26796                 tree.registerNode(this);
26797             }
26798         }
26799     },
26800
26801     /**
26802      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26803      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26804      * @return {String} The path
26805      */
26806     getPath : function(attr){
26807         attr = attr || "id";
26808         var p = this.parentNode;
26809         var b = [this.attributes[attr]];
26810         while(p){
26811             b.unshift(p.attributes[attr]);
26812             p = p.parentNode;
26813         }
26814         var sep = this.getOwnerTree().pathSeparator;
26815         return sep + b.join(sep);
26816     },
26817
26818     /**
26819      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26820      * function call will be the scope provided or the current node. The arguments to the function
26821      * will be the args provided or the current node. If the function returns false at any point,
26822      * the bubble is stopped.
26823      * @param {Function} fn The function to call
26824      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26825      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26826      */
26827     bubble : function(fn, scope, args){
26828         var p = this;
26829         while(p){
26830             if(fn.call(scope || p, args || p) === false){
26831                 break;
26832             }
26833             p = p.parentNode;
26834         }
26835     },
26836
26837     /**
26838      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26839      * function call will be the scope provided or the current node. The arguments to the function
26840      * will be the args provided or the current node. If the function returns false at any point,
26841      * the cascade is stopped on that branch.
26842      * @param {Function} fn The function to call
26843      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26844      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26845      */
26846     cascade : function(fn, scope, args){
26847         if(fn.call(scope || this, args || this) !== false){
26848             var cs = this.childNodes;
26849             for(var i = 0, len = cs.length; i < len; i++) {
26850                 cs[i].cascade(fn, scope, args);
26851             }
26852         }
26853     },
26854
26855     /**
26856      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26857      * function call will be the scope provided or the current node. The arguments to the function
26858      * will be the args provided or the current node. If the function returns false at any point,
26859      * the iteration stops.
26860      * @param {Function} fn The function to call
26861      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26862      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26863      */
26864     eachChild : function(fn, scope, args){
26865         var cs = this.childNodes;
26866         for(var i = 0, len = cs.length; i < len; i++) {
26867                 if(fn.call(scope || this, args || cs[i]) === false){
26868                     break;
26869                 }
26870         }
26871     },
26872
26873     /**
26874      * Finds the first child that has the attribute with the specified value.
26875      * @param {String} attribute The attribute name
26876      * @param {Mixed} value The value to search for
26877      * @return {Node} The found child or null if none was found
26878      */
26879     findChild : function(attribute, value){
26880         var cs = this.childNodes;
26881         for(var i = 0, len = cs.length; i < len; i++) {
26882                 if(cs[i].attributes[attribute] == value){
26883                     return cs[i];
26884                 }
26885         }
26886         return null;
26887     },
26888
26889     /**
26890      * Finds the first child by a custom function. The child matches if the function passed
26891      * returns true.
26892      * @param {Function} fn
26893      * @param {Object} scope (optional)
26894      * @return {Node} The found child or null if none was found
26895      */
26896     findChildBy : function(fn, scope){
26897         var cs = this.childNodes;
26898         for(var i = 0, len = cs.length; i < len; i++) {
26899                 if(fn.call(scope||cs[i], cs[i]) === true){
26900                     return cs[i];
26901                 }
26902         }
26903         return null;
26904     },
26905
26906     /**
26907      * Sorts this nodes children using the supplied sort function
26908      * @param {Function} fn
26909      * @param {Object} scope (optional)
26910      */
26911     sort : function(fn, scope){
26912         var cs = this.childNodes;
26913         var len = cs.length;
26914         if(len > 0){
26915             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26916             cs.sort(sortFn);
26917             for(var i = 0; i < len; i++){
26918                 var n = cs[i];
26919                 n.previousSibling = cs[i-1];
26920                 n.nextSibling = cs[i+1];
26921                 if(i == 0){
26922                     this.setFirstChild(n);
26923                 }
26924                 if(i == len-1){
26925                     this.setLastChild(n);
26926                 }
26927             }
26928         }
26929     },
26930
26931     /**
26932      * Returns true if this node is an ancestor (at any point) of the passed node.
26933      * @param {Node} node
26934      * @return {Boolean}
26935      */
26936     contains : function(node){
26937         return node.isAncestor(this);
26938     },
26939
26940     /**
26941      * Returns true if the passed node is an ancestor (at any point) of this node.
26942      * @param {Node} node
26943      * @return {Boolean}
26944      */
26945     isAncestor : function(node){
26946         var p = this.parentNode;
26947         while(p){
26948             if(p == node){
26949                 return true;
26950             }
26951             p = p.parentNode;
26952         }
26953         return false;
26954     },
26955
26956     toString : function(){
26957         return "[Node"+(this.id?" "+this.id:"")+"]";
26958     }
26959 });/*
26960  * Based on:
26961  * Ext JS Library 1.1.1
26962  * Copyright(c) 2006-2007, Ext JS, LLC.
26963  *
26964  * Originally Released Under LGPL - original licence link has changed is not relivant.
26965  *
26966  * Fork - LGPL
26967  * <script type="text/javascript">
26968  */
26969
26970
26971 /**
26972  * @class Roo.Shadow
26973  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26974  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26975  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26976  * @constructor
26977  * Create a new Shadow
26978  * @param {Object} config The config object
26979  */
26980 Roo.Shadow = function(config){
26981     Roo.apply(this, config);
26982     if(typeof this.mode != "string"){
26983         this.mode = this.defaultMode;
26984     }
26985     var o = this.offset, a = {h: 0};
26986     var rad = Math.floor(this.offset/2);
26987     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26988         case "drop":
26989             a.w = 0;
26990             a.l = a.t = o;
26991             a.t -= 1;
26992             if(Roo.isIE){
26993                 a.l -= this.offset + rad;
26994                 a.t -= this.offset + rad;
26995                 a.w -= rad;
26996                 a.h -= rad;
26997                 a.t += 1;
26998             }
26999         break;
27000         case "sides":
27001             a.w = (o*2);
27002             a.l = -o;
27003             a.t = o-1;
27004             if(Roo.isIE){
27005                 a.l -= (this.offset - rad);
27006                 a.t -= this.offset + rad;
27007                 a.l += 1;
27008                 a.w -= (this.offset - rad)*2;
27009                 a.w -= rad + 1;
27010                 a.h -= 1;
27011             }
27012         break;
27013         case "frame":
27014             a.w = a.h = (o*2);
27015             a.l = a.t = -o;
27016             a.t += 1;
27017             a.h -= 2;
27018             if(Roo.isIE){
27019                 a.l -= (this.offset - rad);
27020                 a.t -= (this.offset - rad);
27021                 a.l += 1;
27022                 a.w -= (this.offset + rad + 1);
27023                 a.h -= (this.offset + rad);
27024                 a.h += 1;
27025             }
27026         break;
27027     };
27028
27029     this.adjusts = a;
27030 };
27031
27032 Roo.Shadow.prototype = {
27033     /**
27034      * @cfg {String} mode
27035      * The shadow display mode.  Supports the following options:<br />
27036      * sides: Shadow displays on both sides and bottom only<br />
27037      * frame: Shadow displays equally on all four sides<br />
27038      * drop: Traditional bottom-right drop shadow (default)
27039      */
27040     mode: false,
27041     /**
27042      * @cfg {String} offset
27043      * The number of pixels to offset the shadow from the element (defaults to 4)
27044      */
27045     offset: 4,
27046
27047     // private
27048     defaultMode: "drop",
27049
27050     /**
27051      * Displays the shadow under the target element
27052      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27053      */
27054     show : function(target){
27055         target = Roo.get(target);
27056         if(!this.el){
27057             this.el = Roo.Shadow.Pool.pull();
27058             if(this.el.dom.nextSibling != target.dom){
27059                 this.el.insertBefore(target);
27060             }
27061         }
27062         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27063         if(Roo.isIE){
27064             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27065         }
27066         this.realign(
27067             target.getLeft(true),
27068             target.getTop(true),
27069             target.getWidth(),
27070             target.getHeight()
27071         );
27072         this.el.dom.style.display = "block";
27073     },
27074
27075     /**
27076      * Returns true if the shadow is visible, else false
27077      */
27078     isVisible : function(){
27079         return this.el ? true : false;  
27080     },
27081
27082     /**
27083      * Direct alignment when values are already available. Show must be called at least once before
27084      * calling this method to ensure it is initialized.
27085      * @param {Number} left The target element left position
27086      * @param {Number} top The target element top position
27087      * @param {Number} width The target element width
27088      * @param {Number} height The target element height
27089      */
27090     realign : function(l, t, w, h){
27091         if(!this.el){
27092             return;
27093         }
27094         var a = this.adjusts, d = this.el.dom, s = d.style;
27095         var iea = 0;
27096         s.left = (l+a.l)+"px";
27097         s.top = (t+a.t)+"px";
27098         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27099  
27100         if(s.width != sws || s.height != shs){
27101             s.width = sws;
27102             s.height = shs;
27103             if(!Roo.isIE){
27104                 var cn = d.childNodes;
27105                 var sww = Math.max(0, (sw-12))+"px";
27106                 cn[0].childNodes[1].style.width = sww;
27107                 cn[1].childNodes[1].style.width = sww;
27108                 cn[2].childNodes[1].style.width = sww;
27109                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27110             }
27111         }
27112     },
27113
27114     /**
27115      * Hides this shadow
27116      */
27117     hide : function(){
27118         if(this.el){
27119             this.el.dom.style.display = "none";
27120             Roo.Shadow.Pool.push(this.el);
27121             delete this.el;
27122         }
27123     },
27124
27125     /**
27126      * Adjust the z-index of this shadow
27127      * @param {Number} zindex The new z-index
27128      */
27129     setZIndex : function(z){
27130         this.zIndex = z;
27131         if(this.el){
27132             this.el.setStyle("z-index", z);
27133         }
27134     }
27135 };
27136
27137 // Private utility class that manages the internal Shadow cache
27138 Roo.Shadow.Pool = function(){
27139     var p = [];
27140     var markup = Roo.isIE ?
27141                  '<div class="x-ie-shadow"></div>' :
27142                  '<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>';
27143     return {
27144         pull : function(){
27145             var sh = p.shift();
27146             if(!sh){
27147                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27148                 sh.autoBoxAdjust = false;
27149             }
27150             return sh;
27151         },
27152
27153         push : function(sh){
27154             p.push(sh);
27155         }
27156     };
27157 }();/*
27158  * Based on:
27159  * Ext JS Library 1.1.1
27160  * Copyright(c) 2006-2007, Ext JS, LLC.
27161  *
27162  * Originally Released Under LGPL - original licence link has changed is not relivant.
27163  *
27164  * Fork - LGPL
27165  * <script type="text/javascript">
27166  */
27167
27168
27169 /**
27170  * @class Roo.SplitBar
27171  * @extends Roo.util.Observable
27172  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27173  * <br><br>
27174  * Usage:
27175  * <pre><code>
27176 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27177                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27178 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27179 split.minSize = 100;
27180 split.maxSize = 600;
27181 split.animate = true;
27182 split.on('moved', splitterMoved);
27183 </code></pre>
27184  * @constructor
27185  * Create a new SplitBar
27186  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27187  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27188  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27189  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27190                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27191                         position of the SplitBar).
27192  */
27193 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27194     
27195     /** @private */
27196     this.el = Roo.get(dragElement, true);
27197     this.el.dom.unselectable = "on";
27198     /** @private */
27199     this.resizingEl = Roo.get(resizingElement, true);
27200
27201     /**
27202      * @private
27203      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27204      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27205      * @type Number
27206      */
27207     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27208     
27209     /**
27210      * The minimum size of the resizing element. (Defaults to 0)
27211      * @type Number
27212      */
27213     this.minSize = 0;
27214     
27215     /**
27216      * The maximum size of the resizing element. (Defaults to 2000)
27217      * @type Number
27218      */
27219     this.maxSize = 2000;
27220     
27221     /**
27222      * Whether to animate the transition to the new size
27223      * @type Boolean
27224      */
27225     this.animate = false;
27226     
27227     /**
27228      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27229      * @type Boolean
27230      */
27231     this.useShim = false;
27232     
27233     /** @private */
27234     this.shim = null;
27235     
27236     if(!existingProxy){
27237         /** @private */
27238         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27239     }else{
27240         this.proxy = Roo.get(existingProxy).dom;
27241     }
27242     /** @private */
27243     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27244     
27245     /** @private */
27246     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27247     
27248     /** @private */
27249     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27250     
27251     /** @private */
27252     this.dragSpecs = {};
27253     
27254     /**
27255      * @private The adapter to use to positon and resize elements
27256      */
27257     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27258     this.adapter.init(this);
27259     
27260     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27261         /** @private */
27262         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27263         this.el.addClass("x-splitbar-h");
27264     }else{
27265         /** @private */
27266         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27267         this.el.addClass("x-splitbar-v");
27268     }
27269     
27270     this.addEvents({
27271         /**
27272          * @event resize
27273          * Fires when the splitter is moved (alias for {@link #event-moved})
27274          * @param {Roo.SplitBar} this
27275          * @param {Number} newSize the new width or height
27276          */
27277         "resize" : true,
27278         /**
27279          * @event moved
27280          * Fires when the splitter is moved
27281          * @param {Roo.SplitBar} this
27282          * @param {Number} newSize the new width or height
27283          */
27284         "moved" : true,
27285         /**
27286          * @event beforeresize
27287          * Fires before the splitter is dragged
27288          * @param {Roo.SplitBar} this
27289          */
27290         "beforeresize" : true,
27291
27292         "beforeapply" : true
27293     });
27294
27295     Roo.util.Observable.call(this);
27296 };
27297
27298 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27299     onStartProxyDrag : function(x, y){
27300         this.fireEvent("beforeresize", this);
27301         if(!this.overlay){
27302             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27303             o.unselectable();
27304             o.enableDisplayMode("block");
27305             // all splitbars share the same overlay
27306             Roo.SplitBar.prototype.overlay = o;
27307         }
27308         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27309         this.overlay.show();
27310         Roo.get(this.proxy).setDisplayed("block");
27311         var size = this.adapter.getElementSize(this);
27312         this.activeMinSize = this.getMinimumSize();;
27313         this.activeMaxSize = this.getMaximumSize();;
27314         var c1 = size - this.activeMinSize;
27315         var c2 = Math.max(this.activeMaxSize - size, 0);
27316         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27317             this.dd.resetConstraints();
27318             this.dd.setXConstraint(
27319                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27320                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27321             );
27322             this.dd.setYConstraint(0, 0);
27323         }else{
27324             this.dd.resetConstraints();
27325             this.dd.setXConstraint(0, 0);
27326             this.dd.setYConstraint(
27327                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27328                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27329             );
27330          }
27331         this.dragSpecs.startSize = size;
27332         this.dragSpecs.startPoint = [x, y];
27333         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27334     },
27335     
27336     /** 
27337      * @private Called after the drag operation by the DDProxy
27338      */
27339     onEndProxyDrag : function(e){
27340         Roo.get(this.proxy).setDisplayed(false);
27341         var endPoint = Roo.lib.Event.getXY(e);
27342         if(this.overlay){
27343             this.overlay.hide();
27344         }
27345         var newSize;
27346         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27347             newSize = this.dragSpecs.startSize + 
27348                 (this.placement == Roo.SplitBar.LEFT ?
27349                     endPoint[0] - this.dragSpecs.startPoint[0] :
27350                     this.dragSpecs.startPoint[0] - endPoint[0]
27351                 );
27352         }else{
27353             newSize = this.dragSpecs.startSize + 
27354                 (this.placement == Roo.SplitBar.TOP ?
27355                     endPoint[1] - this.dragSpecs.startPoint[1] :
27356                     this.dragSpecs.startPoint[1] - endPoint[1]
27357                 );
27358         }
27359         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27360         if(newSize != this.dragSpecs.startSize){
27361             if(this.fireEvent('beforeapply', this, newSize) !== false){
27362                 this.adapter.setElementSize(this, newSize);
27363                 this.fireEvent("moved", this, newSize);
27364                 this.fireEvent("resize", this, newSize);
27365             }
27366         }
27367     },
27368     
27369     /**
27370      * Get the adapter this SplitBar uses
27371      * @return The adapter object
27372      */
27373     getAdapter : function(){
27374         return this.adapter;
27375     },
27376     
27377     /**
27378      * Set the adapter this SplitBar uses
27379      * @param {Object} adapter A SplitBar adapter object
27380      */
27381     setAdapter : function(adapter){
27382         this.adapter = adapter;
27383         this.adapter.init(this);
27384     },
27385     
27386     /**
27387      * Gets the minimum size for the resizing element
27388      * @return {Number} The minimum size
27389      */
27390     getMinimumSize : function(){
27391         return this.minSize;
27392     },
27393     
27394     /**
27395      * Sets the minimum size for the resizing element
27396      * @param {Number} minSize The minimum size
27397      */
27398     setMinimumSize : function(minSize){
27399         this.minSize = minSize;
27400     },
27401     
27402     /**
27403      * Gets the maximum size for the resizing element
27404      * @return {Number} The maximum size
27405      */
27406     getMaximumSize : function(){
27407         return this.maxSize;
27408     },
27409     
27410     /**
27411      * Sets the maximum size for the resizing element
27412      * @param {Number} maxSize The maximum size
27413      */
27414     setMaximumSize : function(maxSize){
27415         this.maxSize = maxSize;
27416     },
27417     
27418     /**
27419      * Sets the initialize size for the resizing element
27420      * @param {Number} size The initial size
27421      */
27422     setCurrentSize : function(size){
27423         var oldAnimate = this.animate;
27424         this.animate = false;
27425         this.adapter.setElementSize(this, size);
27426         this.animate = oldAnimate;
27427     },
27428     
27429     /**
27430      * Destroy this splitbar. 
27431      * @param {Boolean} removeEl True to remove the element
27432      */
27433     destroy : function(removeEl){
27434         if(this.shim){
27435             this.shim.remove();
27436         }
27437         this.dd.unreg();
27438         this.proxy.parentNode.removeChild(this.proxy);
27439         if(removeEl){
27440             this.el.remove();
27441         }
27442     }
27443 });
27444
27445 /**
27446  * @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.
27447  */
27448 Roo.SplitBar.createProxy = function(dir){
27449     var proxy = new Roo.Element(document.createElement("div"));
27450     proxy.unselectable();
27451     var cls = 'x-splitbar-proxy';
27452     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27453     document.body.appendChild(proxy.dom);
27454     return proxy.dom;
27455 };
27456
27457 /** 
27458  * @class Roo.SplitBar.BasicLayoutAdapter
27459  * Default Adapter. It assumes the splitter and resizing element are not positioned
27460  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27461  */
27462 Roo.SplitBar.BasicLayoutAdapter = function(){
27463 };
27464
27465 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27466     // do nothing for now
27467     init : function(s){
27468     
27469     },
27470     /**
27471      * Called before drag operations to get the current size of the resizing element. 
27472      * @param {Roo.SplitBar} s The SplitBar using this adapter
27473      */
27474      getElementSize : function(s){
27475         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27476             return s.resizingEl.getWidth();
27477         }else{
27478             return s.resizingEl.getHeight();
27479         }
27480     },
27481     
27482     /**
27483      * Called after drag operations to set the size of the resizing element.
27484      * @param {Roo.SplitBar} s The SplitBar using this adapter
27485      * @param {Number} newSize The new size to set
27486      * @param {Function} onComplete A function to be invoked when resizing is complete
27487      */
27488     setElementSize : function(s, newSize, onComplete){
27489         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27490             if(!s.animate){
27491                 s.resizingEl.setWidth(newSize);
27492                 if(onComplete){
27493                     onComplete(s, newSize);
27494                 }
27495             }else{
27496                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27497             }
27498         }else{
27499             
27500             if(!s.animate){
27501                 s.resizingEl.setHeight(newSize);
27502                 if(onComplete){
27503                     onComplete(s, newSize);
27504                 }
27505             }else{
27506                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27507             }
27508         }
27509     }
27510 };
27511
27512 /** 
27513  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27514  * @extends Roo.SplitBar.BasicLayoutAdapter
27515  * Adapter that  moves the splitter element to align with the resized sizing element. 
27516  * Used with an absolute positioned SplitBar.
27517  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27518  * document.body, make sure you assign an id to the body element.
27519  */
27520 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27521     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27522     this.container = Roo.get(container);
27523 };
27524
27525 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27526     init : function(s){
27527         this.basic.init(s);
27528     },
27529     
27530     getElementSize : function(s){
27531         return this.basic.getElementSize(s);
27532     },
27533     
27534     setElementSize : function(s, newSize, onComplete){
27535         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27536     },
27537     
27538     moveSplitter : function(s){
27539         var yes = Roo.SplitBar;
27540         switch(s.placement){
27541             case yes.LEFT:
27542                 s.el.setX(s.resizingEl.getRight());
27543                 break;
27544             case yes.RIGHT:
27545                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27546                 break;
27547             case yes.TOP:
27548                 s.el.setY(s.resizingEl.getBottom());
27549                 break;
27550             case yes.BOTTOM:
27551                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27552                 break;
27553         }
27554     }
27555 };
27556
27557 /**
27558  * Orientation constant - Create a vertical SplitBar
27559  * @static
27560  * @type Number
27561  */
27562 Roo.SplitBar.VERTICAL = 1;
27563
27564 /**
27565  * Orientation constant - Create a horizontal SplitBar
27566  * @static
27567  * @type Number
27568  */
27569 Roo.SplitBar.HORIZONTAL = 2;
27570
27571 /**
27572  * Placement constant - The resizing element is to the left of the splitter element
27573  * @static
27574  * @type Number
27575  */
27576 Roo.SplitBar.LEFT = 1;
27577
27578 /**
27579  * Placement constant - The resizing element is to the right of the splitter element
27580  * @static
27581  * @type Number
27582  */
27583 Roo.SplitBar.RIGHT = 2;
27584
27585 /**
27586  * Placement constant - The resizing element is positioned above the splitter element
27587  * @static
27588  * @type Number
27589  */
27590 Roo.SplitBar.TOP = 3;
27591
27592 /**
27593  * Placement constant - The resizing element is positioned under splitter element
27594  * @static
27595  * @type Number
27596  */
27597 Roo.SplitBar.BOTTOM = 4;
27598 /*
27599  * Based on:
27600  * Ext JS Library 1.1.1
27601  * Copyright(c) 2006-2007, Ext JS, LLC.
27602  *
27603  * Originally Released Under LGPL - original licence link has changed is not relivant.
27604  *
27605  * Fork - LGPL
27606  * <script type="text/javascript">
27607  */
27608
27609 /**
27610  * @class Roo.View
27611  * @extends Roo.util.Observable
27612  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27613  * This class also supports single and multi selection modes. <br>
27614  * Create a data model bound view:
27615  <pre><code>
27616  var store = new Roo.data.Store(...);
27617
27618  var view = new Roo.View({
27619     el : "my-element",
27620     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27621  
27622     singleSelect: true,
27623     selectedClass: "ydataview-selected",
27624     store: store
27625  });
27626
27627  // listen for node click?
27628  view.on("click", function(vw, index, node, e){
27629  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27630  });
27631
27632  // load XML data
27633  dataModel.load("foobar.xml");
27634  </code></pre>
27635  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27636  * <br><br>
27637  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27638  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27639  * 
27640  * Note: old style constructor is still suported (container, template, config)
27641  * 
27642  * @constructor
27643  * Create a new View
27644  * @param {Object} config The config object
27645  * 
27646  */
27647 Roo.View = function(config, depreciated_tpl, depreciated_config){
27648     
27649     this.parent = false;
27650     
27651     if (typeof(depreciated_tpl) == 'undefined') {
27652         // new way.. - universal constructor.
27653         Roo.apply(this, config);
27654         this.el  = Roo.get(this.el);
27655     } else {
27656         // old format..
27657         this.el  = Roo.get(config);
27658         this.tpl = depreciated_tpl;
27659         Roo.apply(this, depreciated_config);
27660     }
27661     this.wrapEl  = this.el.wrap().wrap();
27662     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27663     
27664     
27665     if(typeof(this.tpl) == "string"){
27666         this.tpl = new Roo.Template(this.tpl);
27667     } else {
27668         // support xtype ctors..
27669         this.tpl = new Roo.factory(this.tpl, Roo);
27670     }
27671     
27672     
27673     this.tpl.compile();
27674     
27675     /** @private */
27676     this.addEvents({
27677         /**
27678          * @event beforeclick
27679          * Fires before a click is processed. Returns false to cancel the default action.
27680          * @param {Roo.View} this
27681          * @param {Number} index The index of the target node
27682          * @param {HTMLElement} node The target node
27683          * @param {Roo.EventObject} e The raw event object
27684          */
27685             "beforeclick" : true,
27686         /**
27687          * @event click
27688          * Fires when a template node is clicked.
27689          * @param {Roo.View} this
27690          * @param {Number} index The index of the target node
27691          * @param {HTMLElement} node The target node
27692          * @param {Roo.EventObject} e The raw event object
27693          */
27694             "click" : true,
27695         /**
27696          * @event dblclick
27697          * Fires when a template node is double clicked.
27698          * @param {Roo.View} this
27699          * @param {Number} index The index of the target node
27700          * @param {HTMLElement} node The target node
27701          * @param {Roo.EventObject} e The raw event object
27702          */
27703             "dblclick" : true,
27704         /**
27705          * @event contextmenu
27706          * Fires when a template node is right clicked.
27707          * @param {Roo.View} this
27708          * @param {Number} index The index of the target node
27709          * @param {HTMLElement} node The target node
27710          * @param {Roo.EventObject} e The raw event object
27711          */
27712             "contextmenu" : true,
27713         /**
27714          * @event selectionchange
27715          * Fires when the selected nodes change.
27716          * @param {Roo.View} this
27717          * @param {Array} selections Array of the selected nodes
27718          */
27719             "selectionchange" : true,
27720     
27721         /**
27722          * @event beforeselect
27723          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27724          * @param {Roo.View} this
27725          * @param {HTMLElement} node The node to be selected
27726          * @param {Array} selections Array of currently selected nodes
27727          */
27728             "beforeselect" : true,
27729         /**
27730          * @event preparedata
27731          * Fires on every row to render, to allow you to change the data.
27732          * @param {Roo.View} this
27733          * @param {Object} data to be rendered (change this)
27734          */
27735           "preparedata" : true
27736           
27737           
27738         });
27739
27740
27741
27742     this.el.on({
27743         "click": this.onClick,
27744         "dblclick": this.onDblClick,
27745         "contextmenu": this.onContextMenu,
27746         scope:this
27747     });
27748
27749     this.selections = [];
27750     this.nodes = [];
27751     this.cmp = new Roo.CompositeElementLite([]);
27752     if(this.store){
27753         this.store = Roo.factory(this.store, Roo.data);
27754         this.setStore(this.store, true);
27755     }
27756     
27757     if ( this.footer && this.footer.xtype) {
27758            
27759          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27760         
27761         this.footer.dataSource = this.store;
27762         this.footer.container = fctr;
27763         this.footer = Roo.factory(this.footer, Roo);
27764         fctr.insertFirst(this.el);
27765         
27766         // this is a bit insane - as the paging toolbar seems to detach the el..
27767 //        dom.parentNode.parentNode.parentNode
27768          // they get detached?
27769     }
27770     
27771     
27772     Roo.View.superclass.constructor.call(this);
27773     
27774     
27775 };
27776
27777 Roo.extend(Roo.View, Roo.util.Observable, {
27778     
27779      /**
27780      * @cfg {Roo.data.Store} store Data store to load data from.
27781      */
27782     store : false,
27783     
27784     /**
27785      * @cfg {String|Roo.Element} el The container element.
27786      */
27787     el : '',
27788     
27789     /**
27790      * @cfg {String|Roo.Template} tpl The template used by this View 
27791      */
27792     tpl : false,
27793     /**
27794      * @cfg {String} dataName the named area of the template to use as the data area
27795      *                          Works with domtemplates roo-name="name"
27796      */
27797     dataName: false,
27798     /**
27799      * @cfg {String} selectedClass The css class to add to selected nodes
27800      */
27801     selectedClass : "x-view-selected",
27802      /**
27803      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27804      */
27805     emptyText : "",
27806     
27807     /**
27808      * @cfg {String} text to display on mask (default Loading)
27809      */
27810     mask : false,
27811     /**
27812      * @cfg {Boolean} multiSelect Allow multiple selection
27813      */
27814     multiSelect : false,
27815     /**
27816      * @cfg {Boolean} singleSelect Allow single selection
27817      */
27818     singleSelect:  false,
27819     
27820     /**
27821      * @cfg {Boolean} toggleSelect - selecting 
27822      */
27823     toggleSelect : false,
27824     
27825     /**
27826      * @cfg {Boolean} tickable - selecting 
27827      */
27828     tickable : false,
27829     
27830     /**
27831      * Returns the element this view is bound to.
27832      * @return {Roo.Element}
27833      */
27834     getEl : function(){
27835         return this.wrapEl;
27836     },
27837     
27838     
27839
27840     /**
27841      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27842      */
27843     refresh : function(){
27844         //Roo.log('refresh');
27845         var t = this.tpl;
27846         
27847         // if we are using something like 'domtemplate', then
27848         // the what gets used is:
27849         // t.applySubtemplate(NAME, data, wrapping data..)
27850         // the outer template then get' applied with
27851         //     the store 'extra data'
27852         // and the body get's added to the
27853         //      roo-name="data" node?
27854         //      <span class='roo-tpl-{name}'></span> ?????
27855         
27856         
27857         
27858         this.clearSelections();
27859         this.el.update("");
27860         var html = [];
27861         var records = this.store.getRange();
27862         if(records.length < 1) {
27863             
27864             // is this valid??  = should it render a template??
27865             
27866             this.el.update(this.emptyText);
27867             return;
27868         }
27869         var el = this.el;
27870         if (this.dataName) {
27871             this.el.update(t.apply(this.store.meta)); //????
27872             el = this.el.child('.roo-tpl-' + this.dataName);
27873         }
27874         
27875         for(var i = 0, len = records.length; i < len; i++){
27876             var data = this.prepareData(records[i].data, i, records[i]);
27877             this.fireEvent("preparedata", this, data, i, records[i]);
27878             
27879             var d = Roo.apply({}, data);
27880             
27881             if(this.tickable){
27882                 Roo.apply(d, {'roo-id' : Roo.id()});
27883                 
27884                 var _this = this;
27885             
27886                 Roo.each(this.parent.item, function(item){
27887                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27888                         return;
27889                     }
27890                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27891                 });
27892             }
27893             
27894             html[html.length] = Roo.util.Format.trim(
27895                 this.dataName ?
27896                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27897                     t.apply(d)
27898             );
27899         }
27900         
27901         
27902         
27903         el.update(html.join(""));
27904         this.nodes = el.dom.childNodes;
27905         this.updateIndexes(0);
27906     },
27907     
27908
27909     /**
27910      * Function to override to reformat the data that is sent to
27911      * the template for each node.
27912      * DEPRICATED - use the preparedata event handler.
27913      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27914      * a JSON object for an UpdateManager bound view).
27915      */
27916     prepareData : function(data, index, record)
27917     {
27918         this.fireEvent("preparedata", this, data, index, record);
27919         return data;
27920     },
27921
27922     onUpdate : function(ds, record){
27923         // Roo.log('on update');   
27924         this.clearSelections();
27925         var index = this.store.indexOf(record);
27926         var n = this.nodes[index];
27927         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27928         n.parentNode.removeChild(n);
27929         this.updateIndexes(index, index);
27930     },
27931
27932     
27933     
27934 // --------- FIXME     
27935     onAdd : function(ds, records, index)
27936     {
27937         //Roo.log(['on Add', ds, records, index] );        
27938         this.clearSelections();
27939         if(this.nodes.length == 0){
27940             this.refresh();
27941             return;
27942         }
27943         var n = this.nodes[index];
27944         for(var i = 0, len = records.length; i < len; i++){
27945             var d = this.prepareData(records[i].data, i, records[i]);
27946             if(n){
27947                 this.tpl.insertBefore(n, d);
27948             }else{
27949                 
27950                 this.tpl.append(this.el, d);
27951             }
27952         }
27953         this.updateIndexes(index);
27954     },
27955
27956     onRemove : function(ds, record, index){
27957        // Roo.log('onRemove');
27958         this.clearSelections();
27959         var el = this.dataName  ?
27960             this.el.child('.roo-tpl-' + this.dataName) :
27961             this.el; 
27962         
27963         el.dom.removeChild(this.nodes[index]);
27964         this.updateIndexes(index);
27965     },
27966
27967     /**
27968      * Refresh an individual node.
27969      * @param {Number} index
27970      */
27971     refreshNode : function(index){
27972         this.onUpdate(this.store, this.store.getAt(index));
27973     },
27974
27975     updateIndexes : function(startIndex, endIndex){
27976         var ns = this.nodes;
27977         startIndex = startIndex || 0;
27978         endIndex = endIndex || ns.length - 1;
27979         for(var i = startIndex; i <= endIndex; i++){
27980             ns[i].nodeIndex = i;
27981         }
27982     },
27983
27984     /**
27985      * Changes the data store this view uses and refresh the view.
27986      * @param {Store} store
27987      */
27988     setStore : function(store, initial){
27989         if(!initial && this.store){
27990             this.store.un("datachanged", this.refresh);
27991             this.store.un("add", this.onAdd);
27992             this.store.un("remove", this.onRemove);
27993             this.store.un("update", this.onUpdate);
27994             this.store.un("clear", this.refresh);
27995             this.store.un("beforeload", this.onBeforeLoad);
27996             this.store.un("load", this.onLoad);
27997             this.store.un("loadexception", this.onLoad);
27998         }
27999         if(store){
28000           
28001             store.on("datachanged", this.refresh, this);
28002             store.on("add", this.onAdd, this);
28003             store.on("remove", this.onRemove, this);
28004             store.on("update", this.onUpdate, this);
28005             store.on("clear", this.refresh, this);
28006             store.on("beforeload", this.onBeforeLoad, this);
28007             store.on("load", this.onLoad, this);
28008             store.on("loadexception", this.onLoad, this);
28009         }
28010         
28011         if(store){
28012             this.refresh();
28013         }
28014     },
28015     /**
28016      * onbeforeLoad - masks the loading area.
28017      *
28018      */
28019     onBeforeLoad : function(store,opts)
28020     {
28021          //Roo.log('onBeforeLoad');   
28022         if (!opts.add) {
28023             this.el.update("");
28024         }
28025         this.el.mask(this.mask ? this.mask : "Loading" ); 
28026     },
28027     onLoad : function ()
28028     {
28029         this.el.unmask();
28030     },
28031     
28032
28033     /**
28034      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28035      * @param {HTMLElement} node
28036      * @return {HTMLElement} The template node
28037      */
28038     findItemFromChild : function(node){
28039         var el = this.dataName  ?
28040             this.el.child('.roo-tpl-' + this.dataName,true) :
28041             this.el.dom; 
28042         
28043         if(!node || node.parentNode == el){
28044                     return node;
28045             }
28046             var p = node.parentNode;
28047             while(p && p != el){
28048             if(p.parentNode == el){
28049                 return p;
28050             }
28051             p = p.parentNode;
28052         }
28053             return null;
28054     },
28055
28056     /** @ignore */
28057     onClick : function(e){
28058         var item = this.findItemFromChild(e.getTarget());
28059         if(item){
28060             var index = this.indexOf(item);
28061             if(this.onItemClick(item, index, e) !== false){
28062                 this.fireEvent("click", this, index, item, e);
28063             }
28064         }else{
28065             this.clearSelections();
28066         }
28067     },
28068
28069     /** @ignore */
28070     onContextMenu : function(e){
28071         var item = this.findItemFromChild(e.getTarget());
28072         if(item){
28073             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28074         }
28075     },
28076
28077     /** @ignore */
28078     onDblClick : function(e){
28079         var item = this.findItemFromChild(e.getTarget());
28080         if(item){
28081             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28082         }
28083     },
28084
28085     onItemClick : function(item, index, e)
28086     {
28087         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28088             return false;
28089         }
28090         if (this.toggleSelect) {
28091             var m = this.isSelected(item) ? 'unselect' : 'select';
28092             //Roo.log(m);
28093             var _t = this;
28094             _t[m](item, true, false);
28095             return true;
28096         }
28097         if(this.multiSelect || this.singleSelect){
28098             if(this.multiSelect && e.shiftKey && this.lastSelection){
28099                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28100             }else{
28101                 this.select(item, this.multiSelect && e.ctrlKey);
28102                 this.lastSelection = item;
28103             }
28104             
28105             if(!this.tickable){
28106                 e.preventDefault();
28107             }
28108             
28109         }
28110         return true;
28111     },
28112
28113     /**
28114      * Get the number of selected nodes.
28115      * @return {Number}
28116      */
28117     getSelectionCount : function(){
28118         return this.selections.length;
28119     },
28120
28121     /**
28122      * Get the currently selected nodes.
28123      * @return {Array} An array of HTMLElements
28124      */
28125     getSelectedNodes : function(){
28126         return this.selections;
28127     },
28128
28129     /**
28130      * Get the indexes of the selected nodes.
28131      * @return {Array}
28132      */
28133     getSelectedIndexes : function(){
28134         var indexes = [], s = this.selections;
28135         for(var i = 0, len = s.length; i < len; i++){
28136             indexes.push(s[i].nodeIndex);
28137         }
28138         return indexes;
28139     },
28140
28141     /**
28142      * Clear all selections
28143      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28144      */
28145     clearSelections : function(suppressEvent){
28146         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28147             this.cmp.elements = this.selections;
28148             this.cmp.removeClass(this.selectedClass);
28149             this.selections = [];
28150             if(!suppressEvent){
28151                 this.fireEvent("selectionchange", this, this.selections);
28152             }
28153         }
28154     },
28155
28156     /**
28157      * Returns true if the passed node is selected
28158      * @param {HTMLElement/Number} node The node or node index
28159      * @return {Boolean}
28160      */
28161     isSelected : function(node){
28162         var s = this.selections;
28163         if(s.length < 1){
28164             return false;
28165         }
28166         node = this.getNode(node);
28167         return s.indexOf(node) !== -1;
28168     },
28169
28170     /**
28171      * Selects nodes.
28172      * @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
28173      * @param {Boolean} keepExisting (optional) true to keep existing selections
28174      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28175      */
28176     select : function(nodeInfo, keepExisting, suppressEvent){
28177         if(nodeInfo instanceof Array){
28178             if(!keepExisting){
28179                 this.clearSelections(true);
28180             }
28181             for(var i = 0, len = nodeInfo.length; i < len; i++){
28182                 this.select(nodeInfo[i], true, true);
28183             }
28184             return;
28185         } 
28186         var node = this.getNode(nodeInfo);
28187         if(!node || this.isSelected(node)){
28188             return; // already selected.
28189         }
28190         if(!keepExisting){
28191             this.clearSelections(true);
28192         }
28193         
28194         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28195             Roo.fly(node).addClass(this.selectedClass);
28196             this.selections.push(node);
28197             if(!suppressEvent){
28198                 this.fireEvent("selectionchange", this, this.selections);
28199             }
28200         }
28201         
28202         
28203     },
28204       /**
28205      * Unselects nodes.
28206      * @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
28207      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28208      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28209      */
28210     unselect : function(nodeInfo, keepExisting, suppressEvent)
28211     {
28212         if(nodeInfo instanceof Array){
28213             Roo.each(this.selections, function(s) {
28214                 this.unselect(s, nodeInfo);
28215             }, this);
28216             return;
28217         }
28218         var node = this.getNode(nodeInfo);
28219         if(!node || !this.isSelected(node)){
28220             //Roo.log("not selected");
28221             return; // not selected.
28222         }
28223         // fireevent???
28224         var ns = [];
28225         Roo.each(this.selections, function(s) {
28226             if (s == node ) {
28227                 Roo.fly(node).removeClass(this.selectedClass);
28228
28229                 return;
28230             }
28231             ns.push(s);
28232         },this);
28233         
28234         this.selections= ns;
28235         this.fireEvent("selectionchange", this, this.selections);
28236     },
28237
28238     /**
28239      * Gets a template node.
28240      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28241      * @return {HTMLElement} The node or null if it wasn't found
28242      */
28243     getNode : function(nodeInfo){
28244         if(typeof nodeInfo == "string"){
28245             return document.getElementById(nodeInfo);
28246         }else if(typeof nodeInfo == "number"){
28247             return this.nodes[nodeInfo];
28248         }
28249         return nodeInfo;
28250     },
28251
28252     /**
28253      * Gets a range template nodes.
28254      * @param {Number} startIndex
28255      * @param {Number} endIndex
28256      * @return {Array} An array of nodes
28257      */
28258     getNodes : function(start, end){
28259         var ns = this.nodes;
28260         start = start || 0;
28261         end = typeof end == "undefined" ? ns.length - 1 : end;
28262         var nodes = [];
28263         if(start <= end){
28264             for(var i = start; i <= end; i++){
28265                 nodes.push(ns[i]);
28266             }
28267         } else{
28268             for(var i = start; i >= end; i--){
28269                 nodes.push(ns[i]);
28270             }
28271         }
28272         return nodes;
28273     },
28274
28275     /**
28276      * Finds the index of the passed node
28277      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28278      * @return {Number} The index of the node or -1
28279      */
28280     indexOf : function(node){
28281         node = this.getNode(node);
28282         if(typeof node.nodeIndex == "number"){
28283             return node.nodeIndex;
28284         }
28285         var ns = this.nodes;
28286         for(var i = 0, len = ns.length; i < len; i++){
28287             if(ns[i] == node){
28288                 return i;
28289             }
28290         }
28291         return -1;
28292     }
28293 });
28294 /*
28295  * Based on:
28296  * Ext JS Library 1.1.1
28297  * Copyright(c) 2006-2007, Ext JS, LLC.
28298  *
28299  * Originally Released Under LGPL - original licence link has changed is not relivant.
28300  *
28301  * Fork - LGPL
28302  * <script type="text/javascript">
28303  */
28304
28305 /**
28306  * @class Roo.JsonView
28307  * @extends Roo.View
28308  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28309 <pre><code>
28310 var view = new Roo.JsonView({
28311     container: "my-element",
28312     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28313     multiSelect: true, 
28314     jsonRoot: "data" 
28315 });
28316
28317 // listen for node click?
28318 view.on("click", function(vw, index, node, e){
28319     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28320 });
28321
28322 // direct load of JSON data
28323 view.load("foobar.php");
28324
28325 // Example from my blog list
28326 var tpl = new Roo.Template(
28327     '&lt;div class="entry"&gt;' +
28328     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28329     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28330     "&lt;/div&gt;&lt;hr /&gt;"
28331 );
28332
28333 var moreView = new Roo.JsonView({
28334     container :  "entry-list", 
28335     template : tpl,
28336     jsonRoot: "posts"
28337 });
28338 moreView.on("beforerender", this.sortEntries, this);
28339 moreView.load({
28340     url: "/blog/get-posts.php",
28341     params: "allposts=true",
28342     text: "Loading Blog Entries..."
28343 });
28344 </code></pre>
28345
28346 * Note: old code is supported with arguments : (container, template, config)
28347
28348
28349  * @constructor
28350  * Create a new JsonView
28351  * 
28352  * @param {Object} config The config object
28353  * 
28354  */
28355 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28356     
28357     
28358     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28359
28360     var um = this.el.getUpdateManager();
28361     um.setRenderer(this);
28362     um.on("update", this.onLoad, this);
28363     um.on("failure", this.onLoadException, this);
28364
28365     /**
28366      * @event beforerender
28367      * Fires before rendering of the downloaded JSON data.
28368      * @param {Roo.JsonView} this
28369      * @param {Object} data The JSON data loaded
28370      */
28371     /**
28372      * @event load
28373      * Fires when data is loaded.
28374      * @param {Roo.JsonView} this
28375      * @param {Object} data The JSON data loaded
28376      * @param {Object} response The raw Connect response object
28377      */
28378     /**
28379      * @event loadexception
28380      * Fires when loading fails.
28381      * @param {Roo.JsonView} this
28382      * @param {Object} response The raw Connect response object
28383      */
28384     this.addEvents({
28385         'beforerender' : true,
28386         'load' : true,
28387         'loadexception' : true
28388     });
28389 };
28390 Roo.extend(Roo.JsonView, Roo.View, {
28391     /**
28392      * @type {String} The root property in the loaded JSON object that contains the data
28393      */
28394     jsonRoot : "",
28395
28396     /**
28397      * Refreshes the view.
28398      */
28399     refresh : function(){
28400         this.clearSelections();
28401         this.el.update("");
28402         var html = [];
28403         var o = this.jsonData;
28404         if(o && o.length > 0){
28405             for(var i = 0, len = o.length; i < len; i++){
28406                 var data = this.prepareData(o[i], i, o);
28407                 html[html.length] = this.tpl.apply(data);
28408             }
28409         }else{
28410             html.push(this.emptyText);
28411         }
28412         this.el.update(html.join(""));
28413         this.nodes = this.el.dom.childNodes;
28414         this.updateIndexes(0);
28415     },
28416
28417     /**
28418      * 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.
28419      * @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:
28420      <pre><code>
28421      view.load({
28422          url: "your-url.php",
28423          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28424          callback: yourFunction,
28425          scope: yourObject, //(optional scope)
28426          discardUrl: false,
28427          nocache: false,
28428          text: "Loading...",
28429          timeout: 30,
28430          scripts: false
28431      });
28432      </code></pre>
28433      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28434      * 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.
28435      * @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}
28436      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28437      * @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.
28438      */
28439     load : function(){
28440         var um = this.el.getUpdateManager();
28441         um.update.apply(um, arguments);
28442     },
28443
28444     // note - render is a standard framework call...
28445     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28446     render : function(el, response){
28447         
28448         this.clearSelections();
28449         this.el.update("");
28450         var o;
28451         try{
28452             if (response != '') {
28453                 o = Roo.util.JSON.decode(response.responseText);
28454                 if(this.jsonRoot){
28455                     
28456                     o = o[this.jsonRoot];
28457                 }
28458             }
28459         } catch(e){
28460         }
28461         /**
28462          * The current JSON data or null
28463          */
28464         this.jsonData = o;
28465         this.beforeRender();
28466         this.refresh();
28467     },
28468
28469 /**
28470  * Get the number of records in the current JSON dataset
28471  * @return {Number}
28472  */
28473     getCount : function(){
28474         return this.jsonData ? this.jsonData.length : 0;
28475     },
28476
28477 /**
28478  * Returns the JSON object for the specified node(s)
28479  * @param {HTMLElement/Array} node The node or an array of nodes
28480  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28481  * you get the JSON object for the node
28482  */
28483     getNodeData : function(node){
28484         if(node instanceof Array){
28485             var data = [];
28486             for(var i = 0, len = node.length; i < len; i++){
28487                 data.push(this.getNodeData(node[i]));
28488             }
28489             return data;
28490         }
28491         return this.jsonData[this.indexOf(node)] || null;
28492     },
28493
28494     beforeRender : function(){
28495         this.snapshot = this.jsonData;
28496         if(this.sortInfo){
28497             this.sort.apply(this, this.sortInfo);
28498         }
28499         this.fireEvent("beforerender", this, this.jsonData);
28500     },
28501
28502     onLoad : function(el, o){
28503         this.fireEvent("load", this, this.jsonData, o);
28504     },
28505
28506     onLoadException : function(el, o){
28507         this.fireEvent("loadexception", this, o);
28508     },
28509
28510 /**
28511  * Filter the data by a specific property.
28512  * @param {String} property A property on your JSON objects
28513  * @param {String/RegExp} value Either string that the property values
28514  * should start with, or a RegExp to test against the property
28515  */
28516     filter : function(property, value){
28517         if(this.jsonData){
28518             var data = [];
28519             var ss = this.snapshot;
28520             if(typeof value == "string"){
28521                 var vlen = value.length;
28522                 if(vlen == 0){
28523                     this.clearFilter();
28524                     return;
28525                 }
28526                 value = value.toLowerCase();
28527                 for(var i = 0, len = ss.length; i < len; i++){
28528                     var o = ss[i];
28529                     if(o[property].substr(0, vlen).toLowerCase() == value){
28530                         data.push(o);
28531                     }
28532                 }
28533             } else if(value.exec){ // regex?
28534                 for(var i = 0, len = ss.length; i < len; i++){
28535                     var o = ss[i];
28536                     if(value.test(o[property])){
28537                         data.push(o);
28538                     }
28539                 }
28540             } else{
28541                 return;
28542             }
28543             this.jsonData = data;
28544             this.refresh();
28545         }
28546     },
28547
28548 /**
28549  * Filter by a function. The passed function will be called with each
28550  * object in the current dataset. If the function returns true the value is kept,
28551  * otherwise it is filtered.
28552  * @param {Function} fn
28553  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28554  */
28555     filterBy : function(fn, scope){
28556         if(this.jsonData){
28557             var data = [];
28558             var ss = this.snapshot;
28559             for(var i = 0, len = ss.length; i < len; i++){
28560                 var o = ss[i];
28561                 if(fn.call(scope || this, o)){
28562                     data.push(o);
28563                 }
28564             }
28565             this.jsonData = data;
28566             this.refresh();
28567         }
28568     },
28569
28570 /**
28571  * Clears the current filter.
28572  */
28573     clearFilter : function(){
28574         if(this.snapshot && this.jsonData != this.snapshot){
28575             this.jsonData = this.snapshot;
28576             this.refresh();
28577         }
28578     },
28579
28580
28581 /**
28582  * Sorts the data for this view and refreshes it.
28583  * @param {String} property A property on your JSON objects to sort on
28584  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28585  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28586  */
28587     sort : function(property, dir, sortType){
28588         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28589         if(this.jsonData){
28590             var p = property;
28591             var dsc = dir && dir.toLowerCase() == "desc";
28592             var f = function(o1, o2){
28593                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28594                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28595                 ;
28596                 if(v1 < v2){
28597                     return dsc ? +1 : -1;
28598                 } else if(v1 > v2){
28599                     return dsc ? -1 : +1;
28600                 } else{
28601                     return 0;
28602                 }
28603             };
28604             this.jsonData.sort(f);
28605             this.refresh();
28606             if(this.jsonData != this.snapshot){
28607                 this.snapshot.sort(f);
28608             }
28609         }
28610     }
28611 });/*
28612  * Based on:
28613  * Ext JS Library 1.1.1
28614  * Copyright(c) 2006-2007, Ext JS, LLC.
28615  *
28616  * Originally Released Under LGPL - original licence link has changed is not relivant.
28617  *
28618  * Fork - LGPL
28619  * <script type="text/javascript">
28620  */
28621  
28622
28623 /**
28624  * @class Roo.ColorPalette
28625  * @extends Roo.Component
28626  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28627  * Here's an example of typical usage:
28628  * <pre><code>
28629 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28630 cp.render('my-div');
28631
28632 cp.on('select', function(palette, selColor){
28633     // do something with selColor
28634 });
28635 </code></pre>
28636  * @constructor
28637  * Create a new ColorPalette
28638  * @param {Object} config The config object
28639  */
28640 Roo.ColorPalette = function(config){
28641     Roo.ColorPalette.superclass.constructor.call(this, config);
28642     this.addEvents({
28643         /**
28644              * @event select
28645              * Fires when a color is selected
28646              * @param {ColorPalette} this
28647              * @param {String} color The 6-digit color hex code (without the # symbol)
28648              */
28649         select: true
28650     });
28651
28652     if(this.handler){
28653         this.on("select", this.handler, this.scope, true);
28654     }
28655 };
28656 Roo.extend(Roo.ColorPalette, Roo.Component, {
28657     /**
28658      * @cfg {String} itemCls
28659      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28660      */
28661     itemCls : "x-color-palette",
28662     /**
28663      * @cfg {String} value
28664      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28665      * the hex codes are case-sensitive.
28666      */
28667     value : null,
28668     clickEvent:'click',
28669     // private
28670     ctype: "Roo.ColorPalette",
28671
28672     /**
28673      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28674      */
28675     allowReselect : false,
28676
28677     /**
28678      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28679      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28680      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28681      * of colors with the width setting until the box is symmetrical.</p>
28682      * <p>You can override individual colors if needed:</p>
28683      * <pre><code>
28684 var cp = new Roo.ColorPalette();
28685 cp.colors[0] = "FF0000";  // change the first box to red
28686 </code></pre>
28687
28688 Or you can provide a custom array of your own for complete control:
28689 <pre><code>
28690 var cp = new Roo.ColorPalette();
28691 cp.colors = ["000000", "993300", "333300"];
28692 </code></pre>
28693      * @type Array
28694      */
28695     colors : [
28696         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28697         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28698         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28699         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28700         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28701     ],
28702
28703     // private
28704     onRender : function(container, position){
28705         var t = new Roo.MasterTemplate(
28706             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28707         );
28708         var c = this.colors;
28709         for(var i = 0, len = c.length; i < len; i++){
28710             t.add([c[i]]);
28711         }
28712         var el = document.createElement("div");
28713         el.className = this.itemCls;
28714         t.overwrite(el);
28715         container.dom.insertBefore(el, position);
28716         this.el = Roo.get(el);
28717         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28718         if(this.clickEvent != 'click'){
28719             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28720         }
28721     },
28722
28723     // private
28724     afterRender : function(){
28725         Roo.ColorPalette.superclass.afterRender.call(this);
28726         if(this.value){
28727             var s = this.value;
28728             this.value = null;
28729             this.select(s);
28730         }
28731     },
28732
28733     // private
28734     handleClick : function(e, t){
28735         e.preventDefault();
28736         if(!this.disabled){
28737             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28738             this.select(c.toUpperCase());
28739         }
28740     },
28741
28742     /**
28743      * Selects the specified color in the palette (fires the select event)
28744      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28745      */
28746     select : function(color){
28747         color = color.replace("#", "");
28748         if(color != this.value || this.allowReselect){
28749             var el = this.el;
28750             if(this.value){
28751                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28752             }
28753             el.child("a.color-"+color).addClass("x-color-palette-sel");
28754             this.value = color;
28755             this.fireEvent("select", this, color);
28756         }
28757     }
28758 });/*
28759  * Based on:
28760  * Ext JS Library 1.1.1
28761  * Copyright(c) 2006-2007, Ext JS, LLC.
28762  *
28763  * Originally Released Under LGPL - original licence link has changed is not relivant.
28764  *
28765  * Fork - LGPL
28766  * <script type="text/javascript">
28767  */
28768  
28769 /**
28770  * @class Roo.DatePicker
28771  * @extends Roo.Component
28772  * Simple date picker class.
28773  * @constructor
28774  * Create a new DatePicker
28775  * @param {Object} config The config object
28776  */
28777 Roo.DatePicker = function(config){
28778     Roo.DatePicker.superclass.constructor.call(this, config);
28779
28780     this.value = config && config.value ?
28781                  config.value.clearTime() : new Date().clearTime();
28782
28783     this.addEvents({
28784         /**
28785              * @event select
28786              * Fires when a date is selected
28787              * @param {DatePicker} this
28788              * @param {Date} date The selected date
28789              */
28790         'select': true,
28791         /**
28792              * @event monthchange
28793              * Fires when the displayed month changes 
28794              * @param {DatePicker} this
28795              * @param {Date} date The selected month
28796              */
28797         'monthchange': true
28798     });
28799
28800     if(this.handler){
28801         this.on("select", this.handler,  this.scope || this);
28802     }
28803     // build the disabledDatesRE
28804     if(!this.disabledDatesRE && this.disabledDates){
28805         var dd = this.disabledDates;
28806         var re = "(?:";
28807         for(var i = 0; i < dd.length; i++){
28808             re += dd[i];
28809             if(i != dd.length-1) {
28810                 re += "|";
28811             }
28812         }
28813         this.disabledDatesRE = new RegExp(re + ")");
28814     }
28815 };
28816
28817 Roo.extend(Roo.DatePicker, Roo.Component, {
28818     /**
28819      * @cfg {String} todayText
28820      * The text to display on the button that selects the current date (defaults to "Today")
28821      */
28822     todayText : "Today",
28823     /**
28824      * @cfg {String} okText
28825      * The text to display on the ok button
28826      */
28827     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28828     /**
28829      * @cfg {String} cancelText
28830      * The text to display on the cancel button
28831      */
28832     cancelText : "Cancel",
28833     /**
28834      * @cfg {String} todayTip
28835      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28836      */
28837     todayTip : "{0} (Spacebar)",
28838     /**
28839      * @cfg {Date} minDate
28840      * Minimum allowable date (JavaScript date object, defaults to null)
28841      */
28842     minDate : null,
28843     /**
28844      * @cfg {Date} maxDate
28845      * Maximum allowable date (JavaScript date object, defaults to null)
28846      */
28847     maxDate : null,
28848     /**
28849      * @cfg {String} minText
28850      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28851      */
28852     minText : "This date is before the minimum date",
28853     /**
28854      * @cfg {String} maxText
28855      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28856      */
28857     maxText : "This date is after the maximum date",
28858     /**
28859      * @cfg {String} format
28860      * The default date format string which can be overriden for localization support.  The format must be
28861      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28862      */
28863     format : "m/d/y",
28864     /**
28865      * @cfg {Array} disabledDays
28866      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28867      */
28868     disabledDays : null,
28869     /**
28870      * @cfg {String} disabledDaysText
28871      * The tooltip to display when the date falls on a disabled day (defaults to "")
28872      */
28873     disabledDaysText : "",
28874     /**
28875      * @cfg {RegExp} disabledDatesRE
28876      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28877      */
28878     disabledDatesRE : null,
28879     /**
28880      * @cfg {String} disabledDatesText
28881      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28882      */
28883     disabledDatesText : "",
28884     /**
28885      * @cfg {Boolean} constrainToViewport
28886      * True to constrain the date picker to the viewport (defaults to true)
28887      */
28888     constrainToViewport : true,
28889     /**
28890      * @cfg {Array} monthNames
28891      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28892      */
28893     monthNames : Date.monthNames,
28894     /**
28895      * @cfg {Array} dayNames
28896      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28897      */
28898     dayNames : Date.dayNames,
28899     /**
28900      * @cfg {String} nextText
28901      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28902      */
28903     nextText: 'Next Month (Control+Right)',
28904     /**
28905      * @cfg {String} prevText
28906      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28907      */
28908     prevText: 'Previous Month (Control+Left)',
28909     /**
28910      * @cfg {String} monthYearText
28911      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28912      */
28913     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28914     /**
28915      * @cfg {Number} startDay
28916      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28917      */
28918     startDay : 0,
28919     /**
28920      * @cfg {Bool} showClear
28921      * Show a clear button (usefull for date form elements that can be blank.)
28922      */
28923     
28924     showClear: false,
28925     
28926     /**
28927      * Sets the value of the date field
28928      * @param {Date} value The date to set
28929      */
28930     setValue : function(value){
28931         var old = this.value;
28932         
28933         if (typeof(value) == 'string') {
28934          
28935             value = Date.parseDate(value, this.format);
28936         }
28937         if (!value) {
28938             value = new Date();
28939         }
28940         
28941         this.value = value.clearTime(true);
28942         if(this.el){
28943             this.update(this.value);
28944         }
28945     },
28946
28947     /**
28948      * Gets the current selected value of the date field
28949      * @return {Date} The selected date
28950      */
28951     getValue : function(){
28952         return this.value;
28953     },
28954
28955     // private
28956     focus : function(){
28957         if(this.el){
28958             this.update(this.activeDate);
28959         }
28960     },
28961
28962     // privateval
28963     onRender : function(container, position){
28964         
28965         var m = [
28966              '<table cellspacing="0">',
28967                 '<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>',
28968                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28969         var dn = this.dayNames;
28970         for(var i = 0; i < 7; i++){
28971             var d = this.startDay+i;
28972             if(d > 6){
28973                 d = d-7;
28974             }
28975             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28976         }
28977         m[m.length] = "</tr></thead><tbody><tr>";
28978         for(var i = 0; i < 42; i++) {
28979             if(i % 7 == 0 && i != 0){
28980                 m[m.length] = "</tr><tr>";
28981             }
28982             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28983         }
28984         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28985             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28986
28987         var el = document.createElement("div");
28988         el.className = "x-date-picker";
28989         el.innerHTML = m.join("");
28990
28991         container.dom.insertBefore(el, position);
28992
28993         this.el = Roo.get(el);
28994         this.eventEl = Roo.get(el.firstChild);
28995
28996         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28997             handler: this.showPrevMonth,
28998             scope: this,
28999             preventDefault:true,
29000             stopDefault:true
29001         });
29002
29003         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29004             handler: this.showNextMonth,
29005             scope: this,
29006             preventDefault:true,
29007             stopDefault:true
29008         });
29009
29010         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29011
29012         this.monthPicker = this.el.down('div.x-date-mp');
29013         this.monthPicker.enableDisplayMode('block');
29014         
29015         var kn = new Roo.KeyNav(this.eventEl, {
29016             "left" : function(e){
29017                 e.ctrlKey ?
29018                     this.showPrevMonth() :
29019                     this.update(this.activeDate.add("d", -1));
29020             },
29021
29022             "right" : function(e){
29023                 e.ctrlKey ?
29024                     this.showNextMonth() :
29025                     this.update(this.activeDate.add("d", 1));
29026             },
29027
29028             "up" : function(e){
29029                 e.ctrlKey ?
29030                     this.showNextYear() :
29031                     this.update(this.activeDate.add("d", -7));
29032             },
29033
29034             "down" : function(e){
29035                 e.ctrlKey ?
29036                     this.showPrevYear() :
29037                     this.update(this.activeDate.add("d", 7));
29038             },
29039
29040             "pageUp" : function(e){
29041                 this.showNextMonth();
29042             },
29043
29044             "pageDown" : function(e){
29045                 this.showPrevMonth();
29046             },
29047
29048             "enter" : function(e){
29049                 e.stopPropagation();
29050                 return true;
29051             },
29052
29053             scope : this
29054         });
29055
29056         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29057
29058         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29059
29060         this.el.unselectable();
29061         
29062         this.cells = this.el.select("table.x-date-inner tbody td");
29063         this.textNodes = this.el.query("table.x-date-inner tbody span");
29064
29065         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29066             text: "&#160;",
29067             tooltip: this.monthYearText
29068         });
29069
29070         this.mbtn.on('click', this.showMonthPicker, this);
29071         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29072
29073
29074         var today = (new Date()).dateFormat(this.format);
29075         
29076         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29077         if (this.showClear) {
29078             baseTb.add( new Roo.Toolbar.Fill());
29079         }
29080         baseTb.add({
29081             text: String.format(this.todayText, today),
29082             tooltip: String.format(this.todayTip, today),
29083             handler: this.selectToday,
29084             scope: this
29085         });
29086         
29087         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29088             
29089         //});
29090         if (this.showClear) {
29091             
29092             baseTb.add( new Roo.Toolbar.Fill());
29093             baseTb.add({
29094                 text: '&#160;',
29095                 cls: 'x-btn-icon x-btn-clear',
29096                 handler: function() {
29097                     //this.value = '';
29098                     this.fireEvent("select", this, '');
29099                 },
29100                 scope: this
29101             });
29102         }
29103         
29104         
29105         if(Roo.isIE){
29106             this.el.repaint();
29107         }
29108         this.update(this.value);
29109     },
29110
29111     createMonthPicker : function(){
29112         if(!this.monthPicker.dom.firstChild){
29113             var buf = ['<table border="0" cellspacing="0">'];
29114             for(var i = 0; i < 6; i++){
29115                 buf.push(
29116                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29117                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29118                     i == 0 ?
29119                     '<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>' :
29120                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29121                 );
29122             }
29123             buf.push(
29124                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29125                     this.okText,
29126                     '</button><button type="button" class="x-date-mp-cancel">',
29127                     this.cancelText,
29128                     '</button></td></tr>',
29129                 '</table>'
29130             );
29131             this.monthPicker.update(buf.join(''));
29132             this.monthPicker.on('click', this.onMonthClick, this);
29133             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29134
29135             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29136             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29137
29138             this.mpMonths.each(function(m, a, i){
29139                 i += 1;
29140                 if((i%2) == 0){
29141                     m.dom.xmonth = 5 + Math.round(i * .5);
29142                 }else{
29143                     m.dom.xmonth = Math.round((i-1) * .5);
29144                 }
29145             });
29146         }
29147     },
29148
29149     showMonthPicker : function(){
29150         this.createMonthPicker();
29151         var size = this.el.getSize();
29152         this.monthPicker.setSize(size);
29153         this.monthPicker.child('table').setSize(size);
29154
29155         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29156         this.updateMPMonth(this.mpSelMonth);
29157         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29158         this.updateMPYear(this.mpSelYear);
29159
29160         this.monthPicker.slideIn('t', {duration:.2});
29161     },
29162
29163     updateMPYear : function(y){
29164         this.mpyear = y;
29165         var ys = this.mpYears.elements;
29166         for(var i = 1; i <= 10; i++){
29167             var td = ys[i-1], y2;
29168             if((i%2) == 0){
29169                 y2 = y + Math.round(i * .5);
29170                 td.firstChild.innerHTML = y2;
29171                 td.xyear = y2;
29172             }else{
29173                 y2 = y - (5-Math.round(i * .5));
29174                 td.firstChild.innerHTML = y2;
29175                 td.xyear = y2;
29176             }
29177             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29178         }
29179     },
29180
29181     updateMPMonth : function(sm){
29182         this.mpMonths.each(function(m, a, i){
29183             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29184         });
29185     },
29186
29187     selectMPMonth: function(m){
29188         
29189     },
29190
29191     onMonthClick : function(e, t){
29192         e.stopEvent();
29193         var el = new Roo.Element(t), pn;
29194         if(el.is('button.x-date-mp-cancel')){
29195             this.hideMonthPicker();
29196         }
29197         else if(el.is('button.x-date-mp-ok')){
29198             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29199             this.hideMonthPicker();
29200         }
29201         else if(pn = el.up('td.x-date-mp-month', 2)){
29202             this.mpMonths.removeClass('x-date-mp-sel');
29203             pn.addClass('x-date-mp-sel');
29204             this.mpSelMonth = pn.dom.xmonth;
29205         }
29206         else if(pn = el.up('td.x-date-mp-year', 2)){
29207             this.mpYears.removeClass('x-date-mp-sel');
29208             pn.addClass('x-date-mp-sel');
29209             this.mpSelYear = pn.dom.xyear;
29210         }
29211         else if(el.is('a.x-date-mp-prev')){
29212             this.updateMPYear(this.mpyear-10);
29213         }
29214         else if(el.is('a.x-date-mp-next')){
29215             this.updateMPYear(this.mpyear+10);
29216         }
29217     },
29218
29219     onMonthDblClick : function(e, t){
29220         e.stopEvent();
29221         var el = new Roo.Element(t), pn;
29222         if(pn = el.up('td.x-date-mp-month', 2)){
29223             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29224             this.hideMonthPicker();
29225         }
29226         else if(pn = el.up('td.x-date-mp-year', 2)){
29227             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29228             this.hideMonthPicker();
29229         }
29230     },
29231
29232     hideMonthPicker : function(disableAnim){
29233         if(this.monthPicker){
29234             if(disableAnim === true){
29235                 this.monthPicker.hide();
29236             }else{
29237                 this.monthPicker.slideOut('t', {duration:.2});
29238             }
29239         }
29240     },
29241
29242     // private
29243     showPrevMonth : function(e){
29244         this.update(this.activeDate.add("mo", -1));
29245     },
29246
29247     // private
29248     showNextMonth : function(e){
29249         this.update(this.activeDate.add("mo", 1));
29250     },
29251
29252     // private
29253     showPrevYear : function(){
29254         this.update(this.activeDate.add("y", -1));
29255     },
29256
29257     // private
29258     showNextYear : function(){
29259         this.update(this.activeDate.add("y", 1));
29260     },
29261
29262     // private
29263     handleMouseWheel : function(e){
29264         var delta = e.getWheelDelta();
29265         if(delta > 0){
29266             this.showPrevMonth();
29267             e.stopEvent();
29268         } else if(delta < 0){
29269             this.showNextMonth();
29270             e.stopEvent();
29271         }
29272     },
29273
29274     // private
29275     handleDateClick : function(e, t){
29276         e.stopEvent();
29277         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29278             this.setValue(new Date(t.dateValue));
29279             this.fireEvent("select", this, this.value);
29280         }
29281     },
29282
29283     // private
29284     selectToday : function(){
29285         this.setValue(new Date().clearTime());
29286         this.fireEvent("select", this, this.value);
29287     },
29288
29289     // private
29290     update : function(date)
29291     {
29292         var vd = this.activeDate;
29293         this.activeDate = date;
29294         if(vd && this.el){
29295             var t = date.getTime();
29296             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29297                 this.cells.removeClass("x-date-selected");
29298                 this.cells.each(function(c){
29299                    if(c.dom.firstChild.dateValue == t){
29300                        c.addClass("x-date-selected");
29301                        setTimeout(function(){
29302                             try{c.dom.firstChild.focus();}catch(e){}
29303                        }, 50);
29304                        return false;
29305                    }
29306                 });
29307                 return;
29308             }
29309         }
29310         
29311         var days = date.getDaysInMonth();
29312         var firstOfMonth = date.getFirstDateOfMonth();
29313         var startingPos = firstOfMonth.getDay()-this.startDay;
29314
29315         if(startingPos <= this.startDay){
29316             startingPos += 7;
29317         }
29318
29319         var pm = date.add("mo", -1);
29320         var prevStart = pm.getDaysInMonth()-startingPos;
29321
29322         var cells = this.cells.elements;
29323         var textEls = this.textNodes;
29324         days += startingPos;
29325
29326         // convert everything to numbers so it's fast
29327         var day = 86400000;
29328         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29329         var today = new Date().clearTime().getTime();
29330         var sel = date.clearTime().getTime();
29331         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29332         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29333         var ddMatch = this.disabledDatesRE;
29334         var ddText = this.disabledDatesText;
29335         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29336         var ddaysText = this.disabledDaysText;
29337         var format = this.format;
29338
29339         var setCellClass = function(cal, cell){
29340             cell.title = "";
29341             var t = d.getTime();
29342             cell.firstChild.dateValue = t;
29343             if(t == today){
29344                 cell.className += " x-date-today";
29345                 cell.title = cal.todayText;
29346             }
29347             if(t == sel){
29348                 cell.className += " x-date-selected";
29349                 setTimeout(function(){
29350                     try{cell.firstChild.focus();}catch(e){}
29351                 }, 50);
29352             }
29353             // disabling
29354             if(t < min) {
29355                 cell.className = " x-date-disabled";
29356                 cell.title = cal.minText;
29357                 return;
29358             }
29359             if(t > max) {
29360                 cell.className = " x-date-disabled";
29361                 cell.title = cal.maxText;
29362                 return;
29363             }
29364             if(ddays){
29365                 if(ddays.indexOf(d.getDay()) != -1){
29366                     cell.title = ddaysText;
29367                     cell.className = " x-date-disabled";
29368                 }
29369             }
29370             if(ddMatch && format){
29371                 var fvalue = d.dateFormat(format);
29372                 if(ddMatch.test(fvalue)){
29373                     cell.title = ddText.replace("%0", fvalue);
29374                     cell.className = " x-date-disabled";
29375                 }
29376             }
29377         };
29378
29379         var i = 0;
29380         for(; i < startingPos; i++) {
29381             textEls[i].innerHTML = (++prevStart);
29382             d.setDate(d.getDate()+1);
29383             cells[i].className = "x-date-prevday";
29384             setCellClass(this, cells[i]);
29385         }
29386         for(; i < days; i++){
29387             intDay = i - startingPos + 1;
29388             textEls[i].innerHTML = (intDay);
29389             d.setDate(d.getDate()+1);
29390             cells[i].className = "x-date-active";
29391             setCellClass(this, cells[i]);
29392         }
29393         var extraDays = 0;
29394         for(; i < 42; i++) {
29395              textEls[i].innerHTML = (++extraDays);
29396              d.setDate(d.getDate()+1);
29397              cells[i].className = "x-date-nextday";
29398              setCellClass(this, cells[i]);
29399         }
29400
29401         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29402         this.fireEvent('monthchange', this, date);
29403         
29404         if(!this.internalRender){
29405             var main = this.el.dom.firstChild;
29406             var w = main.offsetWidth;
29407             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29408             Roo.fly(main).setWidth(w);
29409             this.internalRender = true;
29410             // opera does not respect the auto grow header center column
29411             // then, after it gets a width opera refuses to recalculate
29412             // without a second pass
29413             if(Roo.isOpera && !this.secondPass){
29414                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29415                 this.secondPass = true;
29416                 this.update.defer(10, this, [date]);
29417             }
29418         }
29419         
29420         
29421     }
29422 });        /*
29423  * Based on:
29424  * Ext JS Library 1.1.1
29425  * Copyright(c) 2006-2007, Ext JS, LLC.
29426  *
29427  * Originally Released Under LGPL - original licence link has changed is not relivant.
29428  *
29429  * Fork - LGPL
29430  * <script type="text/javascript">
29431  */
29432 /**
29433  * @class Roo.TabPanel
29434  * @extends Roo.util.Observable
29435  * A lightweight tab container.
29436  * <br><br>
29437  * Usage:
29438  * <pre><code>
29439 // basic tabs 1, built from existing content
29440 var tabs = new Roo.TabPanel("tabs1");
29441 tabs.addTab("script", "View Script");
29442 tabs.addTab("markup", "View Markup");
29443 tabs.activate("script");
29444
29445 // more advanced tabs, built from javascript
29446 var jtabs = new Roo.TabPanel("jtabs");
29447 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29448
29449 // set up the UpdateManager
29450 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29451 var updater = tab2.getUpdateManager();
29452 updater.setDefaultUrl("ajax1.htm");
29453 tab2.on('activate', updater.refresh, updater, true);
29454
29455 // Use setUrl for Ajax loading
29456 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29457 tab3.setUrl("ajax2.htm", null, true);
29458
29459 // Disabled tab
29460 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29461 tab4.disable();
29462
29463 jtabs.activate("jtabs-1");
29464  * </code></pre>
29465  * @constructor
29466  * Create a new TabPanel.
29467  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29468  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29469  */
29470 Roo.TabPanel = function(container, config){
29471     /**
29472     * The container element for this TabPanel.
29473     * @type Roo.Element
29474     */
29475     this.el = Roo.get(container, true);
29476     if(config){
29477         if(typeof config == "boolean"){
29478             this.tabPosition = config ? "bottom" : "top";
29479         }else{
29480             Roo.apply(this, config);
29481         }
29482     }
29483     if(this.tabPosition == "bottom"){
29484         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29485         this.el.addClass("x-tabs-bottom");
29486     }
29487     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29488     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29489     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29490     if(Roo.isIE){
29491         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29492     }
29493     if(this.tabPosition != "bottom"){
29494         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29495          * @type Roo.Element
29496          */
29497         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29498         this.el.addClass("x-tabs-top");
29499     }
29500     this.items = [];
29501
29502     this.bodyEl.setStyle("position", "relative");
29503
29504     this.active = null;
29505     this.activateDelegate = this.activate.createDelegate(this);
29506
29507     this.addEvents({
29508         /**
29509          * @event tabchange
29510          * Fires when the active tab changes
29511          * @param {Roo.TabPanel} this
29512          * @param {Roo.TabPanelItem} activePanel The new active tab
29513          */
29514         "tabchange": true,
29515         /**
29516          * @event beforetabchange
29517          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29518          * @param {Roo.TabPanel} this
29519          * @param {Object} e Set cancel to true on this object to cancel the tab change
29520          * @param {Roo.TabPanelItem} tab The tab being changed to
29521          */
29522         "beforetabchange" : true
29523     });
29524
29525     Roo.EventManager.onWindowResize(this.onResize, this);
29526     this.cpad = this.el.getPadding("lr");
29527     this.hiddenCount = 0;
29528
29529
29530     // toolbar on the tabbar support...
29531     if (this.toolbar) {
29532         var tcfg = this.toolbar;
29533         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29534         this.toolbar = new Roo.Toolbar(tcfg);
29535         if (Roo.isSafari) {
29536             var tbl = tcfg.container.child('table', true);
29537             tbl.setAttribute('width', '100%');
29538         }
29539         
29540     }
29541    
29542
29543
29544     Roo.TabPanel.superclass.constructor.call(this);
29545 };
29546
29547 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29548     /*
29549      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29550      */
29551     tabPosition : "top",
29552     /*
29553      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29554      */
29555     currentTabWidth : 0,
29556     /*
29557      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29558      */
29559     minTabWidth : 40,
29560     /*
29561      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29562      */
29563     maxTabWidth : 250,
29564     /*
29565      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29566      */
29567     preferredTabWidth : 175,
29568     /*
29569      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29570      */
29571     resizeTabs : false,
29572     /*
29573      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29574      */
29575     monitorResize : true,
29576     /*
29577      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29578      */
29579     toolbar : false,
29580
29581     /**
29582      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29583      * @param {String} id The id of the div to use <b>or create</b>
29584      * @param {String} text The text for the tab
29585      * @param {String} content (optional) Content to put in the TabPanelItem body
29586      * @param {Boolean} closable (optional) True to create a close icon on the tab
29587      * @return {Roo.TabPanelItem} The created TabPanelItem
29588      */
29589     addTab : function(id, text, content, closable){
29590         var item = new Roo.TabPanelItem(this, id, text, closable);
29591         this.addTabItem(item);
29592         if(content){
29593             item.setContent(content);
29594         }
29595         return item;
29596     },
29597
29598     /**
29599      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29600      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29601      * @return {Roo.TabPanelItem}
29602      */
29603     getTab : function(id){
29604         return this.items[id];
29605     },
29606
29607     /**
29608      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29609      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29610      */
29611     hideTab : function(id){
29612         var t = this.items[id];
29613         if(!t.isHidden()){
29614            t.setHidden(true);
29615            this.hiddenCount++;
29616            this.autoSizeTabs();
29617         }
29618     },
29619
29620     /**
29621      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29622      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29623      */
29624     unhideTab : function(id){
29625         var t = this.items[id];
29626         if(t.isHidden()){
29627            t.setHidden(false);
29628            this.hiddenCount--;
29629            this.autoSizeTabs();
29630         }
29631     },
29632
29633     /**
29634      * Adds an existing {@link Roo.TabPanelItem}.
29635      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29636      */
29637     addTabItem : function(item){
29638         this.items[item.id] = item;
29639         this.items.push(item);
29640         if(this.resizeTabs){
29641            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29642            this.autoSizeTabs();
29643         }else{
29644             item.autoSize();
29645         }
29646     },
29647
29648     /**
29649      * Removes a {@link Roo.TabPanelItem}.
29650      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29651      */
29652     removeTab : function(id){
29653         var items = this.items;
29654         var tab = items[id];
29655         if(!tab) { return; }
29656         var index = items.indexOf(tab);
29657         if(this.active == tab && items.length > 1){
29658             var newTab = this.getNextAvailable(index);
29659             if(newTab) {
29660                 newTab.activate();
29661             }
29662         }
29663         this.stripEl.dom.removeChild(tab.pnode.dom);
29664         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29665             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29666         }
29667         items.splice(index, 1);
29668         delete this.items[tab.id];
29669         tab.fireEvent("close", tab);
29670         tab.purgeListeners();
29671         this.autoSizeTabs();
29672     },
29673
29674     getNextAvailable : function(start){
29675         var items = this.items;
29676         var index = start;
29677         // look for a next tab that will slide over to
29678         // replace the one being removed
29679         while(index < items.length){
29680             var item = items[++index];
29681             if(item && !item.isHidden()){
29682                 return item;
29683             }
29684         }
29685         // if one isn't found select the previous tab (on the left)
29686         index = start;
29687         while(index >= 0){
29688             var item = items[--index];
29689             if(item && !item.isHidden()){
29690                 return item;
29691             }
29692         }
29693         return null;
29694     },
29695
29696     /**
29697      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29698      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29699      */
29700     disableTab : function(id){
29701         var tab = this.items[id];
29702         if(tab && this.active != tab){
29703             tab.disable();
29704         }
29705     },
29706
29707     /**
29708      * Enables a {@link Roo.TabPanelItem} that is disabled.
29709      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29710      */
29711     enableTab : function(id){
29712         var tab = this.items[id];
29713         tab.enable();
29714     },
29715
29716     /**
29717      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29718      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29719      * @return {Roo.TabPanelItem} The TabPanelItem.
29720      */
29721     activate : function(id){
29722         var tab = this.items[id];
29723         if(!tab){
29724             return null;
29725         }
29726         if(tab == this.active || tab.disabled){
29727             return tab;
29728         }
29729         var e = {};
29730         this.fireEvent("beforetabchange", this, e, tab);
29731         if(e.cancel !== true && !tab.disabled){
29732             if(this.active){
29733                 this.active.hide();
29734             }
29735             this.active = this.items[id];
29736             this.active.show();
29737             this.fireEvent("tabchange", this, this.active);
29738         }
29739         return tab;
29740     },
29741
29742     /**
29743      * Gets the active {@link Roo.TabPanelItem}.
29744      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29745      */
29746     getActiveTab : function(){
29747         return this.active;
29748     },
29749
29750     /**
29751      * Updates the tab body element to fit the height of the container element
29752      * for overflow scrolling
29753      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29754      */
29755     syncHeight : function(targetHeight){
29756         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29757         var bm = this.bodyEl.getMargins();
29758         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29759         this.bodyEl.setHeight(newHeight);
29760         return newHeight;
29761     },
29762
29763     onResize : function(){
29764         if(this.monitorResize){
29765             this.autoSizeTabs();
29766         }
29767     },
29768
29769     /**
29770      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29771      */
29772     beginUpdate : function(){
29773         this.updating = true;
29774     },
29775
29776     /**
29777      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29778      */
29779     endUpdate : function(){
29780         this.updating = false;
29781         this.autoSizeTabs();
29782     },
29783
29784     /**
29785      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29786      */
29787     autoSizeTabs : function(){
29788         var count = this.items.length;
29789         var vcount = count - this.hiddenCount;
29790         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29791             return;
29792         }
29793         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29794         var availWidth = Math.floor(w / vcount);
29795         var b = this.stripBody;
29796         if(b.getWidth() > w){
29797             var tabs = this.items;
29798             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29799             if(availWidth < this.minTabWidth){
29800                 /*if(!this.sleft){    // incomplete scrolling code
29801                     this.createScrollButtons();
29802                 }
29803                 this.showScroll();
29804                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29805             }
29806         }else{
29807             if(this.currentTabWidth < this.preferredTabWidth){
29808                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29809             }
29810         }
29811     },
29812
29813     /**
29814      * Returns the number of tabs in this TabPanel.
29815      * @return {Number}
29816      */
29817      getCount : function(){
29818          return this.items.length;
29819      },
29820
29821     /**
29822      * Resizes all the tabs to the passed width
29823      * @param {Number} The new width
29824      */
29825     setTabWidth : function(width){
29826         this.currentTabWidth = width;
29827         for(var i = 0, len = this.items.length; i < len; i++) {
29828                 if(!this.items[i].isHidden()) {
29829                 this.items[i].setWidth(width);
29830             }
29831         }
29832     },
29833
29834     /**
29835      * Destroys this TabPanel
29836      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29837      */
29838     destroy : function(removeEl){
29839         Roo.EventManager.removeResizeListener(this.onResize, this);
29840         for(var i = 0, len = this.items.length; i < len; i++){
29841             this.items[i].purgeListeners();
29842         }
29843         if(removeEl === true){
29844             this.el.update("");
29845             this.el.remove();
29846         }
29847     }
29848 });
29849
29850 /**
29851  * @class Roo.TabPanelItem
29852  * @extends Roo.util.Observable
29853  * Represents an individual item (tab plus body) in a TabPanel.
29854  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29855  * @param {String} id The id of this TabPanelItem
29856  * @param {String} text The text for the tab of this TabPanelItem
29857  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29858  */
29859 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29860     /**
29861      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29862      * @type Roo.TabPanel
29863      */
29864     this.tabPanel = tabPanel;
29865     /**
29866      * The id for this TabPanelItem
29867      * @type String
29868      */
29869     this.id = id;
29870     /** @private */
29871     this.disabled = false;
29872     /** @private */
29873     this.text = text;
29874     /** @private */
29875     this.loaded = false;
29876     this.closable = closable;
29877
29878     /**
29879      * The body element for this TabPanelItem.
29880      * @type Roo.Element
29881      */
29882     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29883     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29884     this.bodyEl.setStyle("display", "block");
29885     this.bodyEl.setStyle("zoom", "1");
29886     this.hideAction();
29887
29888     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29889     /** @private */
29890     this.el = Roo.get(els.el, true);
29891     this.inner = Roo.get(els.inner, true);
29892     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29893     this.pnode = Roo.get(els.el.parentNode, true);
29894     this.el.on("mousedown", this.onTabMouseDown, this);
29895     this.el.on("click", this.onTabClick, this);
29896     /** @private */
29897     if(closable){
29898         var c = Roo.get(els.close, true);
29899         c.dom.title = this.closeText;
29900         c.addClassOnOver("close-over");
29901         c.on("click", this.closeClick, this);
29902      }
29903
29904     this.addEvents({
29905          /**
29906          * @event activate
29907          * Fires when this tab becomes the active tab.
29908          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29909          * @param {Roo.TabPanelItem} this
29910          */
29911         "activate": true,
29912         /**
29913          * @event beforeclose
29914          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29915          * @param {Roo.TabPanelItem} this
29916          * @param {Object} e Set cancel to true on this object to cancel the close.
29917          */
29918         "beforeclose": true,
29919         /**
29920          * @event close
29921          * Fires when this tab is closed.
29922          * @param {Roo.TabPanelItem} this
29923          */
29924          "close": true,
29925         /**
29926          * @event deactivate
29927          * Fires when this tab is no longer the active tab.
29928          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29929          * @param {Roo.TabPanelItem} this
29930          */
29931          "deactivate" : true
29932     });
29933     this.hidden = false;
29934
29935     Roo.TabPanelItem.superclass.constructor.call(this);
29936 };
29937
29938 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29939     purgeListeners : function(){
29940        Roo.util.Observable.prototype.purgeListeners.call(this);
29941        this.el.removeAllListeners();
29942     },
29943     /**
29944      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29945      */
29946     show : function(){
29947         this.pnode.addClass("on");
29948         this.showAction();
29949         if(Roo.isOpera){
29950             this.tabPanel.stripWrap.repaint();
29951         }
29952         this.fireEvent("activate", this.tabPanel, this);
29953     },
29954
29955     /**
29956      * Returns true if this tab is the active tab.
29957      * @return {Boolean}
29958      */
29959     isActive : function(){
29960         return this.tabPanel.getActiveTab() == this;
29961     },
29962
29963     /**
29964      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29965      */
29966     hide : function(){
29967         this.pnode.removeClass("on");
29968         this.hideAction();
29969         this.fireEvent("deactivate", this.tabPanel, this);
29970     },
29971
29972     hideAction : function(){
29973         this.bodyEl.hide();
29974         this.bodyEl.setStyle("position", "absolute");
29975         this.bodyEl.setLeft("-20000px");
29976         this.bodyEl.setTop("-20000px");
29977     },
29978
29979     showAction : function(){
29980         this.bodyEl.setStyle("position", "relative");
29981         this.bodyEl.setTop("");
29982         this.bodyEl.setLeft("");
29983         this.bodyEl.show();
29984     },
29985
29986     /**
29987      * Set the tooltip for the tab.
29988      * @param {String} tooltip The tab's tooltip
29989      */
29990     setTooltip : function(text){
29991         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29992             this.textEl.dom.qtip = text;
29993             this.textEl.dom.removeAttribute('title');
29994         }else{
29995             this.textEl.dom.title = text;
29996         }
29997     },
29998
29999     onTabClick : function(e){
30000         e.preventDefault();
30001         this.tabPanel.activate(this.id);
30002     },
30003
30004     onTabMouseDown : function(e){
30005         e.preventDefault();
30006         this.tabPanel.activate(this.id);
30007     },
30008
30009     getWidth : function(){
30010         return this.inner.getWidth();
30011     },
30012
30013     setWidth : function(width){
30014         var iwidth = width - this.pnode.getPadding("lr");
30015         this.inner.setWidth(iwidth);
30016         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30017         this.pnode.setWidth(width);
30018     },
30019
30020     /**
30021      * Show or hide the tab
30022      * @param {Boolean} hidden True to hide or false to show.
30023      */
30024     setHidden : function(hidden){
30025         this.hidden = hidden;
30026         this.pnode.setStyle("display", hidden ? "none" : "");
30027     },
30028
30029     /**
30030      * Returns true if this tab is "hidden"
30031      * @return {Boolean}
30032      */
30033     isHidden : function(){
30034         return this.hidden;
30035     },
30036
30037     /**
30038      * Returns the text for this tab
30039      * @return {String}
30040      */
30041     getText : function(){
30042         return this.text;
30043     },
30044
30045     autoSize : function(){
30046         //this.el.beginMeasure();
30047         this.textEl.setWidth(1);
30048         /*
30049          *  #2804 [new] Tabs in Roojs
30050          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30051          */
30052         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30053         //this.el.endMeasure();
30054     },
30055
30056     /**
30057      * Sets the text for the tab (Note: this also sets the tooltip text)
30058      * @param {String} text The tab's text and tooltip
30059      */
30060     setText : function(text){
30061         this.text = text;
30062         this.textEl.update(text);
30063         this.setTooltip(text);
30064         if(!this.tabPanel.resizeTabs){
30065             this.autoSize();
30066         }
30067     },
30068     /**
30069      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30070      */
30071     activate : function(){
30072         this.tabPanel.activate(this.id);
30073     },
30074
30075     /**
30076      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30077      */
30078     disable : function(){
30079         if(this.tabPanel.active != this){
30080             this.disabled = true;
30081             this.pnode.addClass("disabled");
30082         }
30083     },
30084
30085     /**
30086      * Enables this TabPanelItem if it was previously disabled.
30087      */
30088     enable : function(){
30089         this.disabled = false;
30090         this.pnode.removeClass("disabled");
30091     },
30092
30093     /**
30094      * Sets the content for this TabPanelItem.
30095      * @param {String} content The content
30096      * @param {Boolean} loadScripts true to look for and load scripts
30097      */
30098     setContent : function(content, loadScripts){
30099         this.bodyEl.update(content, loadScripts);
30100     },
30101
30102     /**
30103      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30104      * @return {Roo.UpdateManager} The UpdateManager
30105      */
30106     getUpdateManager : function(){
30107         return this.bodyEl.getUpdateManager();
30108     },
30109
30110     /**
30111      * Set a URL to be used to load the content for this TabPanelItem.
30112      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30113      * @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)
30114      * @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)
30115      * @return {Roo.UpdateManager} The UpdateManager
30116      */
30117     setUrl : function(url, params, loadOnce){
30118         if(this.refreshDelegate){
30119             this.un('activate', this.refreshDelegate);
30120         }
30121         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30122         this.on("activate", this.refreshDelegate);
30123         return this.bodyEl.getUpdateManager();
30124     },
30125
30126     /** @private */
30127     _handleRefresh : function(url, params, loadOnce){
30128         if(!loadOnce || !this.loaded){
30129             var updater = this.bodyEl.getUpdateManager();
30130             updater.update(url, params, this._setLoaded.createDelegate(this));
30131         }
30132     },
30133
30134     /**
30135      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30136      *   Will fail silently if the setUrl method has not been called.
30137      *   This does not activate the panel, just updates its content.
30138      */
30139     refresh : function(){
30140         if(this.refreshDelegate){
30141            this.loaded = false;
30142            this.refreshDelegate();
30143         }
30144     },
30145
30146     /** @private */
30147     _setLoaded : function(){
30148         this.loaded = true;
30149     },
30150
30151     /** @private */
30152     closeClick : function(e){
30153         var o = {};
30154         e.stopEvent();
30155         this.fireEvent("beforeclose", this, o);
30156         if(o.cancel !== true){
30157             this.tabPanel.removeTab(this.id);
30158         }
30159     },
30160     /**
30161      * The text displayed in the tooltip for the close icon.
30162      * @type String
30163      */
30164     closeText : "Close this tab"
30165 });
30166
30167 /** @private */
30168 Roo.TabPanel.prototype.createStrip = function(container){
30169     var strip = document.createElement("div");
30170     strip.className = "x-tabs-wrap";
30171     container.appendChild(strip);
30172     return strip;
30173 };
30174 /** @private */
30175 Roo.TabPanel.prototype.createStripList = function(strip){
30176     // div wrapper for retard IE
30177     // returns the "tr" element.
30178     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30179         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30180         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30181     return strip.firstChild.firstChild.firstChild.firstChild;
30182 };
30183 /** @private */
30184 Roo.TabPanel.prototype.createBody = function(container){
30185     var body = document.createElement("div");
30186     Roo.id(body, "tab-body");
30187     Roo.fly(body).addClass("x-tabs-body");
30188     container.appendChild(body);
30189     return body;
30190 };
30191 /** @private */
30192 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30193     var body = Roo.getDom(id);
30194     if(!body){
30195         body = document.createElement("div");
30196         body.id = id;
30197     }
30198     Roo.fly(body).addClass("x-tabs-item-body");
30199     bodyEl.insertBefore(body, bodyEl.firstChild);
30200     return body;
30201 };
30202 /** @private */
30203 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30204     var td = document.createElement("td");
30205     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30206     //stripEl.appendChild(td);
30207     if(closable){
30208         td.className = "x-tabs-closable";
30209         if(!this.closeTpl){
30210             this.closeTpl = new Roo.Template(
30211                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30212                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30213                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30214             );
30215         }
30216         var el = this.closeTpl.overwrite(td, {"text": text});
30217         var close = el.getElementsByTagName("div")[0];
30218         var inner = el.getElementsByTagName("em")[0];
30219         return {"el": el, "close": close, "inner": inner};
30220     } else {
30221         if(!this.tabTpl){
30222             this.tabTpl = new Roo.Template(
30223                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30224                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30225             );
30226         }
30227         var el = this.tabTpl.overwrite(td, {"text": text});
30228         var inner = el.getElementsByTagName("em")[0];
30229         return {"el": el, "inner": inner};
30230     }
30231 };/*
30232  * Based on:
30233  * Ext JS Library 1.1.1
30234  * Copyright(c) 2006-2007, Ext JS, LLC.
30235  *
30236  * Originally Released Under LGPL - original licence link has changed is not relivant.
30237  *
30238  * Fork - LGPL
30239  * <script type="text/javascript">
30240  */
30241
30242 /**
30243  * @class Roo.Button
30244  * @extends Roo.util.Observable
30245  * Simple Button class
30246  * @cfg {String} text The button text
30247  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30248  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30249  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30250  * @cfg {Object} scope The scope of the handler
30251  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30252  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30253  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30254  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30255  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30256  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30257    applies if enableToggle = true)
30258  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30259  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30260   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30261  * @constructor
30262  * Create a new button
30263  * @param {Object} config The config object
30264  */
30265 Roo.Button = function(renderTo, config)
30266 {
30267     if (!config) {
30268         config = renderTo;
30269         renderTo = config.renderTo || false;
30270     }
30271     
30272     Roo.apply(this, config);
30273     this.addEvents({
30274         /**
30275              * @event click
30276              * Fires when this button is clicked
30277              * @param {Button} this
30278              * @param {EventObject} e The click event
30279              */
30280             "click" : true,
30281         /**
30282              * @event toggle
30283              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30284              * @param {Button} this
30285              * @param {Boolean} pressed
30286              */
30287             "toggle" : true,
30288         /**
30289              * @event mouseover
30290              * Fires when the mouse hovers over the button
30291              * @param {Button} this
30292              * @param {Event} e The event object
30293              */
30294         'mouseover' : true,
30295         /**
30296              * @event mouseout
30297              * Fires when the mouse exits the button
30298              * @param {Button} this
30299              * @param {Event} e The event object
30300              */
30301         'mouseout': true,
30302          /**
30303              * @event render
30304              * Fires when the button is rendered
30305              * @param {Button} this
30306              */
30307         'render': true
30308     });
30309     if(this.menu){
30310         this.menu = Roo.menu.MenuMgr.get(this.menu);
30311     }
30312     // register listeners first!!  - so render can be captured..
30313     Roo.util.Observable.call(this);
30314     if(renderTo){
30315         this.render(renderTo);
30316     }
30317     
30318   
30319 };
30320
30321 Roo.extend(Roo.Button, Roo.util.Observable, {
30322     /**
30323      * 
30324      */
30325     
30326     /**
30327      * Read-only. True if this button is hidden
30328      * @type Boolean
30329      */
30330     hidden : false,
30331     /**
30332      * Read-only. True if this button is disabled
30333      * @type Boolean
30334      */
30335     disabled : false,
30336     /**
30337      * Read-only. True if this button is pressed (only if enableToggle = true)
30338      * @type Boolean
30339      */
30340     pressed : false,
30341
30342     /**
30343      * @cfg {Number} tabIndex 
30344      * The DOM tabIndex for this button (defaults to undefined)
30345      */
30346     tabIndex : undefined,
30347
30348     /**
30349      * @cfg {Boolean} enableToggle
30350      * True to enable pressed/not pressed toggling (defaults to false)
30351      */
30352     enableToggle: false,
30353     /**
30354      * @cfg {Roo.menu.Menu} menu
30355      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30356      */
30357     menu : undefined,
30358     /**
30359      * @cfg {String} menuAlign
30360      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30361      */
30362     menuAlign : "tl-bl?",
30363
30364     /**
30365      * @cfg {String} iconCls
30366      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30367      */
30368     iconCls : undefined,
30369     /**
30370      * @cfg {String} type
30371      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30372      */
30373     type : 'button',
30374
30375     // private
30376     menuClassTarget: 'tr',
30377
30378     /**
30379      * @cfg {String} clickEvent
30380      * The type of event to map to the button's event handler (defaults to 'click')
30381      */
30382     clickEvent : 'click',
30383
30384     /**
30385      * @cfg {Boolean} handleMouseEvents
30386      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30387      */
30388     handleMouseEvents : true,
30389
30390     /**
30391      * @cfg {String} tooltipType
30392      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30393      */
30394     tooltipType : 'qtip',
30395
30396     /**
30397      * @cfg {String} cls
30398      * A CSS class to apply to the button's main element.
30399      */
30400     
30401     /**
30402      * @cfg {Roo.Template} template (Optional)
30403      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30404      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30405      * require code modifications if required elements (e.g. a button) aren't present.
30406      */
30407
30408     // private
30409     render : function(renderTo){
30410         var btn;
30411         if(this.hideParent){
30412             this.parentEl = Roo.get(renderTo);
30413         }
30414         if(!this.dhconfig){
30415             if(!this.template){
30416                 if(!Roo.Button.buttonTemplate){
30417                     // hideous table template
30418                     Roo.Button.buttonTemplate = new Roo.Template(
30419                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30420                         '<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>',
30421                         "</tr></tbody></table>");
30422                 }
30423                 this.template = Roo.Button.buttonTemplate;
30424             }
30425             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30426             var btnEl = btn.child("button:first");
30427             btnEl.on('focus', this.onFocus, this);
30428             btnEl.on('blur', this.onBlur, this);
30429             if(this.cls){
30430                 btn.addClass(this.cls);
30431             }
30432             if(this.icon){
30433                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30434             }
30435             if(this.iconCls){
30436                 btnEl.addClass(this.iconCls);
30437                 if(!this.cls){
30438                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30439                 }
30440             }
30441             if(this.tabIndex !== undefined){
30442                 btnEl.dom.tabIndex = this.tabIndex;
30443             }
30444             if(this.tooltip){
30445                 if(typeof this.tooltip == 'object'){
30446                     Roo.QuickTips.tips(Roo.apply({
30447                           target: btnEl.id
30448                     }, this.tooltip));
30449                 } else {
30450                     btnEl.dom[this.tooltipType] = this.tooltip;
30451                 }
30452             }
30453         }else{
30454             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30455         }
30456         this.el = btn;
30457         if(this.id){
30458             this.el.dom.id = this.el.id = this.id;
30459         }
30460         if(this.menu){
30461             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30462             this.menu.on("show", this.onMenuShow, this);
30463             this.menu.on("hide", this.onMenuHide, this);
30464         }
30465         btn.addClass("x-btn");
30466         if(Roo.isIE && !Roo.isIE7){
30467             this.autoWidth.defer(1, this);
30468         }else{
30469             this.autoWidth();
30470         }
30471         if(this.handleMouseEvents){
30472             btn.on("mouseover", this.onMouseOver, this);
30473             btn.on("mouseout", this.onMouseOut, this);
30474             btn.on("mousedown", this.onMouseDown, this);
30475         }
30476         btn.on(this.clickEvent, this.onClick, this);
30477         //btn.on("mouseup", this.onMouseUp, this);
30478         if(this.hidden){
30479             this.hide();
30480         }
30481         if(this.disabled){
30482             this.disable();
30483         }
30484         Roo.ButtonToggleMgr.register(this);
30485         if(this.pressed){
30486             this.el.addClass("x-btn-pressed");
30487         }
30488         if(this.repeat){
30489             var repeater = new Roo.util.ClickRepeater(btn,
30490                 typeof this.repeat == "object" ? this.repeat : {}
30491             );
30492             repeater.on("click", this.onClick,  this);
30493         }
30494         
30495         this.fireEvent('render', this);
30496         
30497     },
30498     /**
30499      * Returns the button's underlying element
30500      * @return {Roo.Element} The element
30501      */
30502     getEl : function(){
30503         return this.el;  
30504     },
30505     
30506     /**
30507      * Destroys this Button and removes any listeners.
30508      */
30509     destroy : function(){
30510         Roo.ButtonToggleMgr.unregister(this);
30511         this.el.removeAllListeners();
30512         this.purgeListeners();
30513         this.el.remove();
30514     },
30515
30516     // private
30517     autoWidth : function(){
30518         if(this.el){
30519             this.el.setWidth("auto");
30520             if(Roo.isIE7 && Roo.isStrict){
30521                 var ib = this.el.child('button');
30522                 if(ib && ib.getWidth() > 20){
30523                     ib.clip();
30524                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30525                 }
30526             }
30527             if(this.minWidth){
30528                 if(this.hidden){
30529                     this.el.beginMeasure();
30530                 }
30531                 if(this.el.getWidth() < this.minWidth){
30532                     this.el.setWidth(this.minWidth);
30533                 }
30534                 if(this.hidden){
30535                     this.el.endMeasure();
30536                 }
30537             }
30538         }
30539     },
30540
30541     /**
30542      * Assigns this button's click handler
30543      * @param {Function} handler The function to call when the button is clicked
30544      * @param {Object} scope (optional) Scope for the function passed in
30545      */
30546     setHandler : function(handler, scope){
30547         this.handler = handler;
30548         this.scope = scope;  
30549     },
30550     
30551     /**
30552      * Sets this button's text
30553      * @param {String} text The button text
30554      */
30555     setText : function(text){
30556         this.text = text;
30557         if(this.el){
30558             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30559         }
30560         this.autoWidth();
30561     },
30562     
30563     /**
30564      * Gets the text for this button
30565      * @return {String} The button text
30566      */
30567     getText : function(){
30568         return this.text;  
30569     },
30570     
30571     /**
30572      * Show this button
30573      */
30574     show: function(){
30575         this.hidden = false;
30576         if(this.el){
30577             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30578         }
30579     },
30580     
30581     /**
30582      * Hide this button
30583      */
30584     hide: function(){
30585         this.hidden = true;
30586         if(this.el){
30587             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30588         }
30589     },
30590     
30591     /**
30592      * Convenience function for boolean show/hide
30593      * @param {Boolean} visible True to show, false to hide
30594      */
30595     setVisible: function(visible){
30596         if(visible) {
30597             this.show();
30598         }else{
30599             this.hide();
30600         }
30601     },
30602     
30603     /**
30604      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30605      * @param {Boolean} state (optional) Force a particular state
30606      */
30607     toggle : function(state){
30608         state = state === undefined ? !this.pressed : state;
30609         if(state != this.pressed){
30610             if(state){
30611                 this.el.addClass("x-btn-pressed");
30612                 this.pressed = true;
30613                 this.fireEvent("toggle", this, true);
30614             }else{
30615                 this.el.removeClass("x-btn-pressed");
30616                 this.pressed = false;
30617                 this.fireEvent("toggle", this, false);
30618             }
30619             if(this.toggleHandler){
30620                 this.toggleHandler.call(this.scope || this, this, state);
30621             }
30622         }
30623     },
30624     
30625     /**
30626      * Focus the button
30627      */
30628     focus : function(){
30629         this.el.child('button:first').focus();
30630     },
30631     
30632     /**
30633      * Disable this button
30634      */
30635     disable : function(){
30636         if(this.el){
30637             this.el.addClass("x-btn-disabled");
30638         }
30639         this.disabled = true;
30640     },
30641     
30642     /**
30643      * Enable this button
30644      */
30645     enable : function(){
30646         if(this.el){
30647             this.el.removeClass("x-btn-disabled");
30648         }
30649         this.disabled = false;
30650     },
30651
30652     /**
30653      * Convenience function for boolean enable/disable
30654      * @param {Boolean} enabled True to enable, false to disable
30655      */
30656     setDisabled : function(v){
30657         this[v !== true ? "enable" : "disable"]();
30658     },
30659
30660     // private
30661     onClick : function(e)
30662     {
30663         if(e){
30664             e.preventDefault();
30665         }
30666         if(e.button != 0){
30667             return;
30668         }
30669         if(!this.disabled){
30670             if(this.enableToggle){
30671                 this.toggle();
30672             }
30673             if(this.menu && !this.menu.isVisible()){
30674                 this.menu.show(this.el, this.menuAlign);
30675             }
30676             this.fireEvent("click", this, e);
30677             if(this.handler){
30678                 this.el.removeClass("x-btn-over");
30679                 this.handler.call(this.scope || this, this, e);
30680             }
30681         }
30682     },
30683     // private
30684     onMouseOver : function(e){
30685         if(!this.disabled){
30686             this.el.addClass("x-btn-over");
30687             this.fireEvent('mouseover', this, e);
30688         }
30689     },
30690     // private
30691     onMouseOut : function(e){
30692         if(!e.within(this.el,  true)){
30693             this.el.removeClass("x-btn-over");
30694             this.fireEvent('mouseout', this, e);
30695         }
30696     },
30697     // private
30698     onFocus : function(e){
30699         if(!this.disabled){
30700             this.el.addClass("x-btn-focus");
30701         }
30702     },
30703     // private
30704     onBlur : function(e){
30705         this.el.removeClass("x-btn-focus");
30706     },
30707     // private
30708     onMouseDown : function(e){
30709         if(!this.disabled && e.button == 0){
30710             this.el.addClass("x-btn-click");
30711             Roo.get(document).on('mouseup', this.onMouseUp, this);
30712         }
30713     },
30714     // private
30715     onMouseUp : function(e){
30716         if(e.button == 0){
30717             this.el.removeClass("x-btn-click");
30718             Roo.get(document).un('mouseup', this.onMouseUp, this);
30719         }
30720     },
30721     // private
30722     onMenuShow : function(e){
30723         this.el.addClass("x-btn-menu-active");
30724     },
30725     // private
30726     onMenuHide : function(e){
30727         this.el.removeClass("x-btn-menu-active");
30728     }   
30729 });
30730
30731 // Private utility class used by Button
30732 Roo.ButtonToggleMgr = function(){
30733    var groups = {};
30734    
30735    function toggleGroup(btn, state){
30736        if(state){
30737            var g = groups[btn.toggleGroup];
30738            for(var i = 0, l = g.length; i < l; i++){
30739                if(g[i] != btn){
30740                    g[i].toggle(false);
30741                }
30742            }
30743        }
30744    }
30745    
30746    return {
30747        register : function(btn){
30748            if(!btn.toggleGroup){
30749                return;
30750            }
30751            var g = groups[btn.toggleGroup];
30752            if(!g){
30753                g = groups[btn.toggleGroup] = [];
30754            }
30755            g.push(btn);
30756            btn.on("toggle", toggleGroup);
30757        },
30758        
30759        unregister : function(btn){
30760            if(!btn.toggleGroup){
30761                return;
30762            }
30763            var g = groups[btn.toggleGroup];
30764            if(g){
30765                g.remove(btn);
30766                btn.un("toggle", toggleGroup);
30767            }
30768        }
30769    };
30770 }();/*
30771  * Based on:
30772  * Ext JS Library 1.1.1
30773  * Copyright(c) 2006-2007, Ext JS, LLC.
30774  *
30775  * Originally Released Under LGPL - original licence link has changed is not relivant.
30776  *
30777  * Fork - LGPL
30778  * <script type="text/javascript">
30779  */
30780  
30781 /**
30782  * @class Roo.SplitButton
30783  * @extends Roo.Button
30784  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30785  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30786  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30787  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30788  * @cfg {String} arrowTooltip The title attribute of the arrow
30789  * @constructor
30790  * Create a new menu button
30791  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30792  * @param {Object} config The config object
30793  */
30794 Roo.SplitButton = function(renderTo, config){
30795     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30796     /**
30797      * @event arrowclick
30798      * Fires when this button's arrow is clicked
30799      * @param {SplitButton} this
30800      * @param {EventObject} e The click event
30801      */
30802     this.addEvents({"arrowclick":true});
30803 };
30804
30805 Roo.extend(Roo.SplitButton, Roo.Button, {
30806     render : function(renderTo){
30807         // this is one sweet looking template!
30808         var tpl = new Roo.Template(
30809             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30810             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30811             '<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>',
30812             "</tbody></table></td><td>",
30813             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30814             '<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>',
30815             "</tbody></table></td></tr></table>"
30816         );
30817         var btn = tpl.append(renderTo, [this.text, this.type], true);
30818         var btnEl = btn.child("button");
30819         if(this.cls){
30820             btn.addClass(this.cls);
30821         }
30822         if(this.icon){
30823             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30824         }
30825         if(this.iconCls){
30826             btnEl.addClass(this.iconCls);
30827             if(!this.cls){
30828                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30829             }
30830         }
30831         this.el = btn;
30832         if(this.handleMouseEvents){
30833             btn.on("mouseover", this.onMouseOver, this);
30834             btn.on("mouseout", this.onMouseOut, this);
30835             btn.on("mousedown", this.onMouseDown, this);
30836             btn.on("mouseup", this.onMouseUp, this);
30837         }
30838         btn.on(this.clickEvent, this.onClick, this);
30839         if(this.tooltip){
30840             if(typeof this.tooltip == 'object'){
30841                 Roo.QuickTips.tips(Roo.apply({
30842                       target: btnEl.id
30843                 }, this.tooltip));
30844             } else {
30845                 btnEl.dom[this.tooltipType] = this.tooltip;
30846             }
30847         }
30848         if(this.arrowTooltip){
30849             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30850         }
30851         if(this.hidden){
30852             this.hide();
30853         }
30854         if(this.disabled){
30855             this.disable();
30856         }
30857         if(this.pressed){
30858             this.el.addClass("x-btn-pressed");
30859         }
30860         if(Roo.isIE && !Roo.isIE7){
30861             this.autoWidth.defer(1, this);
30862         }else{
30863             this.autoWidth();
30864         }
30865         if(this.menu){
30866             this.menu.on("show", this.onMenuShow, this);
30867             this.menu.on("hide", this.onMenuHide, this);
30868         }
30869         this.fireEvent('render', this);
30870     },
30871
30872     // private
30873     autoWidth : function(){
30874         if(this.el){
30875             var tbl = this.el.child("table:first");
30876             var tbl2 = this.el.child("table:last");
30877             this.el.setWidth("auto");
30878             tbl.setWidth("auto");
30879             if(Roo.isIE7 && Roo.isStrict){
30880                 var ib = this.el.child('button:first');
30881                 if(ib && ib.getWidth() > 20){
30882                     ib.clip();
30883                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30884                 }
30885             }
30886             if(this.minWidth){
30887                 if(this.hidden){
30888                     this.el.beginMeasure();
30889                 }
30890                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30891                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30892                 }
30893                 if(this.hidden){
30894                     this.el.endMeasure();
30895                 }
30896             }
30897             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30898         } 
30899     },
30900     /**
30901      * Sets this button's click handler
30902      * @param {Function} handler The function to call when the button is clicked
30903      * @param {Object} scope (optional) Scope for the function passed above
30904      */
30905     setHandler : function(handler, scope){
30906         this.handler = handler;
30907         this.scope = scope;  
30908     },
30909     
30910     /**
30911      * Sets this button's arrow click handler
30912      * @param {Function} handler The function to call when the arrow is clicked
30913      * @param {Object} scope (optional) Scope for the function passed above
30914      */
30915     setArrowHandler : function(handler, scope){
30916         this.arrowHandler = handler;
30917         this.scope = scope;  
30918     },
30919     
30920     /**
30921      * Focus the button
30922      */
30923     focus : function(){
30924         if(this.el){
30925             this.el.child("button:first").focus();
30926         }
30927     },
30928
30929     // private
30930     onClick : function(e){
30931         e.preventDefault();
30932         if(!this.disabled){
30933             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30934                 if(this.menu && !this.menu.isVisible()){
30935                     this.menu.show(this.el, this.menuAlign);
30936                 }
30937                 this.fireEvent("arrowclick", this, e);
30938                 if(this.arrowHandler){
30939                     this.arrowHandler.call(this.scope || this, this, e);
30940                 }
30941             }else{
30942                 this.fireEvent("click", this, e);
30943                 if(this.handler){
30944                     this.handler.call(this.scope || this, this, e);
30945                 }
30946             }
30947         }
30948     },
30949     // private
30950     onMouseDown : function(e){
30951         if(!this.disabled){
30952             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30953         }
30954     },
30955     // private
30956     onMouseUp : function(e){
30957         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30958     }   
30959 });
30960
30961
30962 // backwards compat
30963 Roo.MenuButton = Roo.SplitButton;/*
30964  * Based on:
30965  * Ext JS Library 1.1.1
30966  * Copyright(c) 2006-2007, Ext JS, LLC.
30967  *
30968  * Originally Released Under LGPL - original licence link has changed is not relivant.
30969  *
30970  * Fork - LGPL
30971  * <script type="text/javascript">
30972  */
30973
30974 /**
30975  * @class Roo.Toolbar
30976  * @children   Roo.Toolbar.Item Roo.form.Field  
30977  * Basic Toolbar class.
30978  * @constructor
30979  * Creates a new Toolbar
30980  * @param {Object} container The config object
30981  */ 
30982 Roo.Toolbar = function(container, buttons, config)
30983 {
30984     /// old consturctor format still supported..
30985     if(container instanceof Array){ // omit the container for later rendering
30986         buttons = container;
30987         config = buttons;
30988         container = null;
30989     }
30990     if (typeof(container) == 'object' && container.xtype) {
30991         config = container;
30992         container = config.container;
30993         buttons = config.buttons || []; // not really - use items!!
30994     }
30995     var xitems = [];
30996     if (config && config.items) {
30997         xitems = config.items;
30998         delete config.items;
30999     }
31000     Roo.apply(this, config);
31001     this.buttons = buttons;
31002     
31003     if(container){
31004         this.render(container);
31005     }
31006     this.xitems = xitems;
31007     Roo.each(xitems, function(b) {
31008         this.add(b);
31009     }, this);
31010     
31011 };
31012
31013 Roo.Toolbar.prototype = {
31014     /**
31015      * @cfg {Array} items
31016      * array of button configs or elements to add (will be converted to a MixedCollection)
31017      */
31018     items: false,
31019     /**
31020      * @cfg {String/HTMLElement/Element} container
31021      * The id or element that will contain the toolbar
31022      */
31023     // private
31024     render : function(ct){
31025         this.el = Roo.get(ct);
31026         if(this.cls){
31027             this.el.addClass(this.cls);
31028         }
31029         // using a table allows for vertical alignment
31030         // 100% width is needed by Safari...
31031         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31032         this.tr = this.el.child("tr", true);
31033         var autoId = 0;
31034         this.items = new Roo.util.MixedCollection(false, function(o){
31035             return o.id || ("item" + (++autoId));
31036         });
31037         if(this.buttons){
31038             this.add.apply(this, this.buttons);
31039             delete this.buttons;
31040         }
31041     },
31042
31043     /**
31044      * Adds element(s) to the toolbar -- this function takes a variable number of 
31045      * arguments of mixed type and adds them to the toolbar.
31046      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31047      * <ul>
31048      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31049      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31050      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31051      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31052      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31053      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31054      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31055      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31056      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31057      * </ul>
31058      * @param {Mixed} arg2
31059      * @param {Mixed} etc.
31060      */
31061     add : function(){
31062         var a = arguments, l = a.length;
31063         for(var i = 0; i < l; i++){
31064             this._add(a[i]);
31065         }
31066     },
31067     // private..
31068     _add : function(el) {
31069         
31070         if (el.xtype) {
31071             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31072         }
31073         
31074         if (el.applyTo){ // some kind of form field
31075             return this.addField(el);
31076         } 
31077         if (el.render){ // some kind of Toolbar.Item
31078             return this.addItem(el);
31079         }
31080         if (typeof el == "string"){ // string
31081             if(el == "separator" || el == "-"){
31082                 return this.addSeparator();
31083             }
31084             if (el == " "){
31085                 return this.addSpacer();
31086             }
31087             if(el == "->"){
31088                 return this.addFill();
31089             }
31090             return this.addText(el);
31091             
31092         }
31093         if(el.tagName){ // element
31094             return this.addElement(el);
31095         }
31096         if(typeof el == "object"){ // must be button config?
31097             return this.addButton(el);
31098         }
31099         // and now what?!?!
31100         return false;
31101         
31102     },
31103     
31104     /**
31105      * Add an Xtype element
31106      * @param {Object} xtype Xtype Object
31107      * @return {Object} created Object
31108      */
31109     addxtype : function(e){
31110         return this.add(e);  
31111     },
31112     
31113     /**
31114      * Returns the Element for this toolbar.
31115      * @return {Roo.Element}
31116      */
31117     getEl : function(){
31118         return this.el;  
31119     },
31120     
31121     /**
31122      * Adds a separator
31123      * @return {Roo.Toolbar.Item} The separator item
31124      */
31125     addSeparator : function(){
31126         return this.addItem(new Roo.Toolbar.Separator());
31127     },
31128
31129     /**
31130      * Adds a spacer element
31131      * @return {Roo.Toolbar.Spacer} The spacer item
31132      */
31133     addSpacer : function(){
31134         return this.addItem(new Roo.Toolbar.Spacer());
31135     },
31136
31137     /**
31138      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31139      * @return {Roo.Toolbar.Fill} The fill item
31140      */
31141     addFill : function(){
31142         return this.addItem(new Roo.Toolbar.Fill());
31143     },
31144
31145     /**
31146      * Adds any standard HTML element to the toolbar
31147      * @param {String/HTMLElement/Element} el The element or id of the element to add
31148      * @return {Roo.Toolbar.Item} The element's item
31149      */
31150     addElement : function(el){
31151         return this.addItem(new Roo.Toolbar.Item(el));
31152     },
31153     /**
31154      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31155      * @type Roo.util.MixedCollection  
31156      */
31157     items : false,
31158      
31159     /**
31160      * Adds any Toolbar.Item or subclass
31161      * @param {Roo.Toolbar.Item} item
31162      * @return {Roo.Toolbar.Item} The item
31163      */
31164     addItem : function(item){
31165         var td = this.nextBlock();
31166         item.render(td);
31167         this.items.add(item);
31168         return item;
31169     },
31170     
31171     /**
31172      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31173      * @param {Object/Array} config A button config or array of configs
31174      * @return {Roo.Toolbar.Button/Array}
31175      */
31176     addButton : function(config){
31177         if(config instanceof Array){
31178             var buttons = [];
31179             for(var i = 0, len = config.length; i < len; i++) {
31180                 buttons.push(this.addButton(config[i]));
31181             }
31182             return buttons;
31183         }
31184         var b = config;
31185         if(!(config instanceof Roo.Toolbar.Button)){
31186             b = config.split ?
31187                 new Roo.Toolbar.SplitButton(config) :
31188                 new Roo.Toolbar.Button(config);
31189         }
31190         var td = this.nextBlock();
31191         b.render(td);
31192         this.items.add(b);
31193         return b;
31194     },
31195     
31196     /**
31197      * Adds text to the toolbar
31198      * @param {String} text The text to add
31199      * @return {Roo.Toolbar.Item} The element's item
31200      */
31201     addText : function(text){
31202         return this.addItem(new Roo.Toolbar.TextItem(text));
31203     },
31204     
31205     /**
31206      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31207      * @param {Number} index The index where the item is to be inserted
31208      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31209      * @return {Roo.Toolbar.Button/Item}
31210      */
31211     insertButton : function(index, item){
31212         if(item instanceof Array){
31213             var buttons = [];
31214             for(var i = 0, len = item.length; i < len; i++) {
31215                buttons.push(this.insertButton(index + i, item[i]));
31216             }
31217             return buttons;
31218         }
31219         if (!(item instanceof Roo.Toolbar.Button)){
31220            item = new Roo.Toolbar.Button(item);
31221         }
31222         var td = document.createElement("td");
31223         this.tr.insertBefore(td, this.tr.childNodes[index]);
31224         item.render(td);
31225         this.items.insert(index, item);
31226         return item;
31227     },
31228     
31229     /**
31230      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31231      * @param {Object} config
31232      * @return {Roo.Toolbar.Item} The element's item
31233      */
31234     addDom : function(config, returnEl){
31235         var td = this.nextBlock();
31236         Roo.DomHelper.overwrite(td, config);
31237         var ti = new Roo.Toolbar.Item(td.firstChild);
31238         ti.render(td);
31239         this.items.add(ti);
31240         return ti;
31241     },
31242
31243     /**
31244      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31245      * @type Roo.util.MixedCollection  
31246      */
31247     fields : false,
31248     
31249     /**
31250      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31251      * Note: the field should not have been rendered yet. For a field that has already been
31252      * rendered, use {@link #addElement}.
31253      * @param {Roo.form.Field} field
31254      * @return {Roo.ToolbarItem}
31255      */
31256      
31257       
31258     addField : function(field) {
31259         if (!this.fields) {
31260             var autoId = 0;
31261             this.fields = new Roo.util.MixedCollection(false, function(o){
31262                 return o.id || ("item" + (++autoId));
31263             });
31264
31265         }
31266         
31267         var td = this.nextBlock();
31268         field.render(td);
31269         var ti = new Roo.Toolbar.Item(td.firstChild);
31270         ti.render(td);
31271         this.items.add(ti);
31272         this.fields.add(field);
31273         return ti;
31274     },
31275     /**
31276      * Hide the toolbar
31277      * @method hide
31278      */
31279      
31280       
31281     hide : function()
31282     {
31283         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31284         this.el.child('div').hide();
31285     },
31286     /**
31287      * Show the toolbar
31288      * @method show
31289      */
31290     show : function()
31291     {
31292         this.el.child('div').show();
31293     },
31294       
31295     // private
31296     nextBlock : function(){
31297         var td = document.createElement("td");
31298         this.tr.appendChild(td);
31299         return td;
31300     },
31301
31302     // private
31303     destroy : function(){
31304         if(this.items){ // rendered?
31305             Roo.destroy.apply(Roo, this.items.items);
31306         }
31307         if(this.fields){ // rendered?
31308             Roo.destroy.apply(Roo, this.fields.items);
31309         }
31310         Roo.Element.uncache(this.el, this.tr);
31311     }
31312 };
31313
31314 /**
31315  * @class Roo.Toolbar.Item
31316  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31317  * @constructor
31318  * Creates a new Item
31319  * @param {HTMLElement} el 
31320  */
31321 Roo.Toolbar.Item = function(el){
31322     var cfg = {};
31323     if (typeof (el.xtype) != 'undefined') {
31324         cfg = el;
31325         el = cfg.el;
31326     }
31327     
31328     this.el = Roo.getDom(el);
31329     this.id = Roo.id(this.el);
31330     this.hidden = false;
31331     
31332     this.addEvents({
31333          /**
31334              * @event render
31335              * Fires when the button is rendered
31336              * @param {Button} this
31337              */
31338         'render': true
31339     });
31340     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31341 };
31342 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31343 //Roo.Toolbar.Item.prototype = {
31344     
31345     /**
31346      * Get this item's HTML Element
31347      * @return {HTMLElement}
31348      */
31349     getEl : function(){
31350        return this.el;  
31351     },
31352
31353     // private
31354     render : function(td){
31355         
31356          this.td = td;
31357         td.appendChild(this.el);
31358         
31359         this.fireEvent('render', this);
31360     },
31361     
31362     /**
31363      * Removes and destroys this item.
31364      */
31365     destroy : function(){
31366         this.td.parentNode.removeChild(this.td);
31367     },
31368     
31369     /**
31370      * Shows this item.
31371      */
31372     show: function(){
31373         this.hidden = false;
31374         this.td.style.display = "";
31375     },
31376     
31377     /**
31378      * Hides this item.
31379      */
31380     hide: function(){
31381         this.hidden = true;
31382         this.td.style.display = "none";
31383     },
31384     
31385     /**
31386      * Convenience function for boolean show/hide.
31387      * @param {Boolean} visible true to show/false to hide
31388      */
31389     setVisible: function(visible){
31390         if(visible) {
31391             this.show();
31392         }else{
31393             this.hide();
31394         }
31395     },
31396     
31397     /**
31398      * Try to focus this item.
31399      */
31400     focus : function(){
31401         Roo.fly(this.el).focus();
31402     },
31403     
31404     /**
31405      * Disables this item.
31406      */
31407     disable : function(){
31408         Roo.fly(this.td).addClass("x-item-disabled");
31409         this.disabled = true;
31410         this.el.disabled = true;
31411     },
31412     
31413     /**
31414      * Enables this item.
31415      */
31416     enable : function(){
31417         Roo.fly(this.td).removeClass("x-item-disabled");
31418         this.disabled = false;
31419         this.el.disabled = false;
31420     }
31421 });
31422
31423
31424 /**
31425  * @class Roo.Toolbar.Separator
31426  * @extends Roo.Toolbar.Item
31427  * A simple toolbar separator class
31428  * @constructor
31429  * Creates a new Separator
31430  */
31431 Roo.Toolbar.Separator = function(cfg){
31432     
31433     var s = document.createElement("span");
31434     s.className = "ytb-sep";
31435     if (cfg) {
31436         cfg.el = s;
31437     }
31438     
31439     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31440 };
31441 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31442     enable:Roo.emptyFn,
31443     disable:Roo.emptyFn,
31444     focus:Roo.emptyFn
31445 });
31446
31447 /**
31448  * @class Roo.Toolbar.Spacer
31449  * @extends Roo.Toolbar.Item
31450  * A simple element that adds extra horizontal space to a toolbar.
31451  * @constructor
31452  * Creates a new Spacer
31453  */
31454 Roo.Toolbar.Spacer = function(cfg){
31455     var s = document.createElement("div");
31456     s.className = "ytb-spacer";
31457     if (cfg) {
31458         cfg.el = s;
31459     }
31460     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31461 };
31462 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31463     enable:Roo.emptyFn,
31464     disable:Roo.emptyFn,
31465     focus:Roo.emptyFn
31466 });
31467
31468 /**
31469  * @class Roo.Toolbar.Fill
31470  * @extends Roo.Toolbar.Spacer
31471  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31472  * @constructor
31473  * Creates a new Spacer
31474  */
31475 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31476     // private
31477     render : function(td){
31478         td.style.width = '100%';
31479         Roo.Toolbar.Fill.superclass.render.call(this, td);
31480     }
31481 });
31482
31483 /**
31484  * @class Roo.Toolbar.TextItem
31485  * @extends Roo.Toolbar.Item
31486  * A simple class that renders text directly into a toolbar.
31487  * @constructor
31488  * Creates a new TextItem
31489  * @cfg {string} text 
31490  */
31491 Roo.Toolbar.TextItem = function(cfg){
31492     var  text = cfg || "";
31493     if (typeof(cfg) == 'object') {
31494         text = cfg.text || "";
31495     }  else {
31496         cfg = null;
31497     }
31498     var s = document.createElement("span");
31499     s.className = "ytb-text";
31500     s.innerHTML = text;
31501     if (cfg) {
31502         cfg.el  = s;
31503     }
31504     
31505     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31506 };
31507 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31508     
31509      
31510     enable:Roo.emptyFn,
31511     disable:Roo.emptyFn,
31512     focus:Roo.emptyFn,
31513      /**
31514      * Shows this button
31515      */
31516     show: function(){
31517         this.hidden = false;
31518         this.el.style.display = "";
31519     },
31520     
31521     /**
31522      * Hides this button
31523      */
31524     hide: function(){
31525         this.hidden = true;
31526         this.el.style.display = "none";
31527     }
31528     
31529 });
31530
31531 /**
31532  * @class Roo.Toolbar.Button
31533  * @extends Roo.Button
31534  * A button that renders into a toolbar.
31535  * @constructor
31536  * Creates a new Button
31537  * @param {Object} config A standard {@link Roo.Button} config object
31538  */
31539 Roo.Toolbar.Button = function(config){
31540     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31541 };
31542 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31543 {
31544     
31545     
31546     render : function(td){
31547         this.td = td;
31548         Roo.Toolbar.Button.superclass.render.call(this, td);
31549     },
31550     
31551     /**
31552      * Removes and destroys this button
31553      */
31554     destroy : function(){
31555         Roo.Toolbar.Button.superclass.destroy.call(this);
31556         this.td.parentNode.removeChild(this.td);
31557     },
31558     
31559     /**
31560      * Shows this button
31561      */
31562     show: function(){
31563         this.hidden = false;
31564         this.td.style.display = "";
31565     },
31566     
31567     /**
31568      * Hides this button
31569      */
31570     hide: function(){
31571         this.hidden = true;
31572         this.td.style.display = "none";
31573     },
31574
31575     /**
31576      * Disables this item
31577      */
31578     disable : function(){
31579         Roo.fly(this.td).addClass("x-item-disabled");
31580         this.disabled = true;
31581     },
31582
31583     /**
31584      * Enables this item
31585      */
31586     enable : function(){
31587         Roo.fly(this.td).removeClass("x-item-disabled");
31588         this.disabled = false;
31589     }
31590 });
31591 // backwards compat
31592 Roo.ToolbarButton = Roo.Toolbar.Button;
31593
31594 /**
31595  * @class Roo.Toolbar.SplitButton
31596  * @extends Roo.SplitButton
31597  * A menu button that renders into a toolbar.
31598  * @constructor
31599  * Creates a new SplitButton
31600  * @param {Object} config A standard {@link Roo.SplitButton} config object
31601  */
31602 Roo.Toolbar.SplitButton = function(config){
31603     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31604 };
31605 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31606     render : function(td){
31607         this.td = td;
31608         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31609     },
31610     
31611     /**
31612      * Removes and destroys this button
31613      */
31614     destroy : function(){
31615         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31616         this.td.parentNode.removeChild(this.td);
31617     },
31618     
31619     /**
31620      * Shows this button
31621      */
31622     show: function(){
31623         this.hidden = false;
31624         this.td.style.display = "";
31625     },
31626     
31627     /**
31628      * Hides this button
31629      */
31630     hide: function(){
31631         this.hidden = true;
31632         this.td.style.display = "none";
31633     }
31634 });
31635
31636 // backwards compat
31637 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31638  * Based on:
31639  * Ext JS Library 1.1.1
31640  * Copyright(c) 2006-2007, Ext JS, LLC.
31641  *
31642  * Originally Released Under LGPL - original licence link has changed is not relivant.
31643  *
31644  * Fork - LGPL
31645  * <script type="text/javascript">
31646  */
31647  
31648 /**
31649  * @class Roo.PagingToolbar
31650  * @extends Roo.Toolbar
31651  * @children   Roo.Toolbar.Item Roo.form.Field
31652  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31653  * @constructor
31654  * Create a new PagingToolbar
31655  * @param {Object} config The config object
31656  */
31657 Roo.PagingToolbar = function(el, ds, config)
31658 {
31659     // old args format still supported... - xtype is prefered..
31660     if (typeof(el) == 'object' && el.xtype) {
31661         // created from xtype...
31662         config = el;
31663         ds = el.dataSource;
31664         el = config.container;
31665     }
31666     var items = [];
31667     if (config.items) {
31668         items = config.items;
31669         config.items = [];
31670     }
31671     
31672     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31673     this.ds = ds;
31674     this.cursor = 0;
31675     this.renderButtons(this.el);
31676     this.bind(ds);
31677     
31678     // supprot items array.
31679    
31680     Roo.each(items, function(e) {
31681         this.add(Roo.factory(e));
31682     },this);
31683     
31684 };
31685
31686 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31687    
31688     /**
31689      * @cfg {String/HTMLElement/Element} container
31690      * container The id or element that will contain the toolbar
31691      */
31692     /**
31693      * @cfg {Boolean} displayInfo
31694      * True to display the displayMsg (defaults to false)
31695      */
31696     
31697     
31698     /**
31699      * @cfg {Number} pageSize
31700      * The number of records to display per page (defaults to 20)
31701      */
31702     pageSize: 20,
31703     /**
31704      * @cfg {String} displayMsg
31705      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31706      */
31707     displayMsg : 'Displaying {0} - {1} of {2}',
31708     /**
31709      * @cfg {String} emptyMsg
31710      * The message to display when no records are found (defaults to "No data to display")
31711      */
31712     emptyMsg : 'No data to display',
31713     /**
31714      * Customizable piece of the default paging text (defaults to "Page")
31715      * @type String
31716      */
31717     beforePageText : "Page",
31718     /**
31719      * Customizable piece of the default paging text (defaults to "of %0")
31720      * @type String
31721      */
31722     afterPageText : "of {0}",
31723     /**
31724      * Customizable piece of the default paging text (defaults to "First Page")
31725      * @type String
31726      */
31727     firstText : "First Page",
31728     /**
31729      * Customizable piece of the default paging text (defaults to "Previous Page")
31730      * @type String
31731      */
31732     prevText : "Previous Page",
31733     /**
31734      * Customizable piece of the default paging text (defaults to "Next Page")
31735      * @type String
31736      */
31737     nextText : "Next Page",
31738     /**
31739      * Customizable piece of the default paging text (defaults to "Last Page")
31740      * @type String
31741      */
31742     lastText : "Last Page",
31743     /**
31744      * Customizable piece of the default paging text (defaults to "Refresh")
31745      * @type String
31746      */
31747     refreshText : "Refresh",
31748
31749     // private
31750     renderButtons : function(el){
31751         Roo.PagingToolbar.superclass.render.call(this, el);
31752         this.first = this.addButton({
31753             tooltip: this.firstText,
31754             cls: "x-btn-icon x-grid-page-first",
31755             disabled: true,
31756             handler: this.onClick.createDelegate(this, ["first"])
31757         });
31758         this.prev = this.addButton({
31759             tooltip: this.prevText,
31760             cls: "x-btn-icon x-grid-page-prev",
31761             disabled: true,
31762             handler: this.onClick.createDelegate(this, ["prev"])
31763         });
31764         //this.addSeparator();
31765         this.add(this.beforePageText);
31766         this.field = Roo.get(this.addDom({
31767            tag: "input",
31768            type: "text",
31769            size: "3",
31770            value: "1",
31771            cls: "x-grid-page-number"
31772         }).el);
31773         this.field.on("keydown", this.onPagingKeydown, this);
31774         this.field.on("focus", function(){this.dom.select();});
31775         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31776         this.field.setHeight(18);
31777         //this.addSeparator();
31778         this.next = this.addButton({
31779             tooltip: this.nextText,
31780             cls: "x-btn-icon x-grid-page-next",
31781             disabled: true,
31782             handler: this.onClick.createDelegate(this, ["next"])
31783         });
31784         this.last = this.addButton({
31785             tooltip: this.lastText,
31786             cls: "x-btn-icon x-grid-page-last",
31787             disabled: true,
31788             handler: this.onClick.createDelegate(this, ["last"])
31789         });
31790         //this.addSeparator();
31791         this.loading = this.addButton({
31792             tooltip: this.refreshText,
31793             cls: "x-btn-icon x-grid-loading",
31794             handler: this.onClick.createDelegate(this, ["refresh"])
31795         });
31796
31797         if(this.displayInfo){
31798             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31799         }
31800     },
31801
31802     // private
31803     updateInfo : function(){
31804         if(this.displayEl){
31805             var count = this.ds.getCount();
31806             var msg = count == 0 ?
31807                 this.emptyMsg :
31808                 String.format(
31809                     this.displayMsg,
31810                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31811                 );
31812             this.displayEl.update(msg);
31813         }
31814     },
31815
31816     // private
31817     onLoad : function(ds, r, o){
31818        this.cursor = o.params ? o.params.start : 0;
31819        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31820
31821        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31822        this.field.dom.value = ap;
31823        this.first.setDisabled(ap == 1);
31824        this.prev.setDisabled(ap == 1);
31825        this.next.setDisabled(ap == ps);
31826        this.last.setDisabled(ap == ps);
31827        this.loading.enable();
31828        this.updateInfo();
31829     },
31830
31831     // private
31832     getPageData : function(){
31833         var total = this.ds.getTotalCount();
31834         return {
31835             total : total,
31836             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31837             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31838         };
31839     },
31840
31841     // private
31842     onLoadError : function(){
31843         this.loading.enable();
31844     },
31845
31846     // private
31847     onPagingKeydown : function(e){
31848         var k = e.getKey();
31849         var d = this.getPageData();
31850         if(k == e.RETURN){
31851             var v = this.field.dom.value, pageNum;
31852             if(!v || isNaN(pageNum = parseInt(v, 10))){
31853                 this.field.dom.value = d.activePage;
31854                 return;
31855             }
31856             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31857             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31858             e.stopEvent();
31859         }
31860         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))
31861         {
31862           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31863           this.field.dom.value = pageNum;
31864           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31865           e.stopEvent();
31866         }
31867         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31868         {
31869           var v = this.field.dom.value, pageNum; 
31870           var increment = (e.shiftKey) ? 10 : 1;
31871           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31872             increment *= -1;
31873           }
31874           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31875             this.field.dom.value = d.activePage;
31876             return;
31877           }
31878           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31879           {
31880             this.field.dom.value = parseInt(v, 10) + increment;
31881             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31882             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31883           }
31884           e.stopEvent();
31885         }
31886     },
31887
31888     // private
31889     beforeLoad : function(){
31890         if(this.loading){
31891             this.loading.disable();
31892         }
31893     },
31894
31895     // private
31896     onClick : function(which){
31897         var ds = this.ds;
31898         switch(which){
31899             case "first":
31900                 ds.load({params:{start: 0, limit: this.pageSize}});
31901             break;
31902             case "prev":
31903                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31904             break;
31905             case "next":
31906                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31907             break;
31908             case "last":
31909                 var total = ds.getTotalCount();
31910                 var extra = total % this.pageSize;
31911                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31912                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31913             break;
31914             case "refresh":
31915                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31916             break;
31917         }
31918     },
31919
31920     /**
31921      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31922      * @param {Roo.data.Store} store The data store to unbind
31923      */
31924     unbind : function(ds){
31925         ds.un("beforeload", this.beforeLoad, this);
31926         ds.un("load", this.onLoad, this);
31927         ds.un("loadexception", this.onLoadError, this);
31928         ds.un("remove", this.updateInfo, this);
31929         ds.un("add", this.updateInfo, this);
31930         this.ds = undefined;
31931     },
31932
31933     /**
31934      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31935      * @param {Roo.data.Store} store The data store to bind
31936      */
31937     bind : function(ds){
31938         ds.on("beforeload", this.beforeLoad, this);
31939         ds.on("load", this.onLoad, this);
31940         ds.on("loadexception", this.onLoadError, this);
31941         ds.on("remove", this.updateInfo, this);
31942         ds.on("add", this.updateInfo, this);
31943         this.ds = ds;
31944     }
31945 });/*
31946  * Based on:
31947  * Ext JS Library 1.1.1
31948  * Copyright(c) 2006-2007, Ext JS, LLC.
31949  *
31950  * Originally Released Under LGPL - original licence link has changed is not relivant.
31951  *
31952  * Fork - LGPL
31953  * <script type="text/javascript">
31954  */
31955
31956 /**
31957  * @class Roo.Resizable
31958  * @extends Roo.util.Observable
31959  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31960  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31961  * 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
31962  * the element will be wrapped for you automatically.</p>
31963  * <p>Here is the list of valid resize handles:</p>
31964  * <pre>
31965 Value   Description
31966 ------  -------------------
31967  'n'     north
31968  's'     south
31969  'e'     east
31970  'w'     west
31971  'nw'    northwest
31972  'sw'    southwest
31973  'se'    southeast
31974  'ne'    northeast
31975  'hd'    horizontal drag
31976  'all'   all
31977 </pre>
31978  * <p>Here's an example showing the creation of a typical Resizable:</p>
31979  * <pre><code>
31980 var resizer = new Roo.Resizable("element-id", {
31981     handles: 'all',
31982     minWidth: 200,
31983     minHeight: 100,
31984     maxWidth: 500,
31985     maxHeight: 400,
31986     pinned: true
31987 });
31988 resizer.on("resize", myHandler);
31989 </code></pre>
31990  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31991  * resizer.east.setDisplayed(false);</p>
31992  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31993  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31994  * resize operation's new size (defaults to [0, 0])
31995  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31996  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31997  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31998  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31999  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32000  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32001  * @cfg {Number} width The width of the element in pixels (defaults to null)
32002  * @cfg {Number} height The height of the element in pixels (defaults to null)
32003  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32004  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32005  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32006  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32007  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32008  * in favor of the handles config option (defaults to false)
32009  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32010  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32011  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32012  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32013  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32014  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32015  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32016  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32017  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32018  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32019  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32020  * @constructor
32021  * Create a new resizable component
32022  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32023  * @param {Object} config configuration options
32024   */
32025 Roo.Resizable = function(el, config)
32026 {
32027     this.el = Roo.get(el);
32028
32029     if(config && config.wrap){
32030         config.resizeChild = this.el;
32031         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32032         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32033         this.el.setStyle("overflow", "hidden");
32034         this.el.setPositioning(config.resizeChild.getPositioning());
32035         config.resizeChild.clearPositioning();
32036         if(!config.width || !config.height){
32037             var csize = config.resizeChild.getSize();
32038             this.el.setSize(csize.width, csize.height);
32039         }
32040         if(config.pinned && !config.adjustments){
32041             config.adjustments = "auto";
32042         }
32043     }
32044
32045     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32046     this.proxy.unselectable();
32047     this.proxy.enableDisplayMode('block');
32048
32049     Roo.apply(this, config);
32050
32051     if(this.pinned){
32052         this.disableTrackOver = true;
32053         this.el.addClass("x-resizable-pinned");
32054     }
32055     // if the element isn't positioned, make it relative
32056     var position = this.el.getStyle("position");
32057     if(position != "absolute" && position != "fixed"){
32058         this.el.setStyle("position", "relative");
32059     }
32060     if(!this.handles){ // no handles passed, must be legacy style
32061         this.handles = 's,e,se';
32062         if(this.multiDirectional){
32063             this.handles += ',n,w';
32064         }
32065     }
32066     if(this.handles == "all"){
32067         this.handles = "n s e w ne nw se sw";
32068     }
32069     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32070     var ps = Roo.Resizable.positions;
32071     for(var i = 0, len = hs.length; i < len; i++){
32072         if(hs[i] && ps[hs[i]]){
32073             var pos = ps[hs[i]];
32074             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32075         }
32076     }
32077     // legacy
32078     this.corner = this.southeast;
32079     
32080     // updateBox = the box can move..
32081     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32082         this.updateBox = true;
32083     }
32084
32085     this.activeHandle = null;
32086
32087     if(this.resizeChild){
32088         if(typeof this.resizeChild == "boolean"){
32089             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32090         }else{
32091             this.resizeChild = Roo.get(this.resizeChild, true);
32092         }
32093     }
32094     
32095     if(this.adjustments == "auto"){
32096         var rc = this.resizeChild;
32097         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32098         if(rc && (hw || hn)){
32099             rc.position("relative");
32100             rc.setLeft(hw ? hw.el.getWidth() : 0);
32101             rc.setTop(hn ? hn.el.getHeight() : 0);
32102         }
32103         this.adjustments = [
32104             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32105             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32106         ];
32107     }
32108
32109     if(this.draggable){
32110         this.dd = this.dynamic ?
32111             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32112         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32113     }
32114
32115     // public events
32116     this.addEvents({
32117         /**
32118          * @event beforeresize
32119          * Fired before resize is allowed. Set enabled to false to cancel resize.
32120          * @param {Roo.Resizable} this
32121          * @param {Roo.EventObject} e The mousedown event
32122          */
32123         "beforeresize" : true,
32124         /**
32125          * @event resizing
32126          * Fired a resizing.
32127          * @param {Roo.Resizable} this
32128          * @param {Number} x The new x position
32129          * @param {Number} y The new y position
32130          * @param {Number} w The new w width
32131          * @param {Number} h The new h hight
32132          * @param {Roo.EventObject} e The mouseup event
32133          */
32134         "resizing" : true,
32135         /**
32136          * @event resize
32137          * Fired after a resize.
32138          * @param {Roo.Resizable} this
32139          * @param {Number} width The new width
32140          * @param {Number} height The new height
32141          * @param {Roo.EventObject} e The mouseup event
32142          */
32143         "resize" : true
32144     });
32145
32146     if(this.width !== null && this.height !== null){
32147         this.resizeTo(this.width, this.height);
32148     }else{
32149         this.updateChildSize();
32150     }
32151     if(Roo.isIE){
32152         this.el.dom.style.zoom = 1;
32153     }
32154     Roo.Resizable.superclass.constructor.call(this);
32155 };
32156
32157 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32158         resizeChild : false,
32159         adjustments : [0, 0],
32160         minWidth : 5,
32161         minHeight : 5,
32162         maxWidth : 10000,
32163         maxHeight : 10000,
32164         enabled : true,
32165         animate : false,
32166         duration : .35,
32167         dynamic : false,
32168         handles : false,
32169         multiDirectional : false,
32170         disableTrackOver : false,
32171         easing : 'easeOutStrong',
32172         widthIncrement : 0,
32173         heightIncrement : 0,
32174         pinned : false,
32175         width : null,
32176         height : null,
32177         preserveRatio : false,
32178         transparent: false,
32179         minX: 0,
32180         minY: 0,
32181         draggable: false,
32182
32183         /**
32184          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32185          */
32186         constrainTo: undefined,
32187         /**
32188          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32189          */
32190         resizeRegion: undefined,
32191
32192
32193     /**
32194      * Perform a manual resize
32195      * @param {Number} width
32196      * @param {Number} height
32197      */
32198     resizeTo : function(width, height){
32199         this.el.setSize(width, height);
32200         this.updateChildSize();
32201         this.fireEvent("resize", this, width, height, null);
32202     },
32203
32204     // private
32205     startSizing : function(e, handle){
32206         this.fireEvent("beforeresize", this, e);
32207         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32208
32209             if(!this.overlay){
32210                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32211                 this.overlay.unselectable();
32212                 this.overlay.enableDisplayMode("block");
32213                 this.overlay.on("mousemove", this.onMouseMove, this);
32214                 this.overlay.on("mouseup", this.onMouseUp, this);
32215             }
32216             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32217
32218             this.resizing = true;
32219             this.startBox = this.el.getBox();
32220             this.startPoint = e.getXY();
32221             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32222                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32223
32224             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32225             this.overlay.show();
32226
32227             if(this.constrainTo) {
32228                 var ct = Roo.get(this.constrainTo);
32229                 this.resizeRegion = ct.getRegion().adjust(
32230                     ct.getFrameWidth('t'),
32231                     ct.getFrameWidth('l'),
32232                     -ct.getFrameWidth('b'),
32233                     -ct.getFrameWidth('r')
32234                 );
32235             }
32236
32237             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32238             this.proxy.show();
32239             this.proxy.setBox(this.startBox);
32240             if(!this.dynamic){
32241                 this.proxy.setStyle('visibility', 'visible');
32242             }
32243         }
32244     },
32245
32246     // private
32247     onMouseDown : function(handle, e){
32248         if(this.enabled){
32249             e.stopEvent();
32250             this.activeHandle = handle;
32251             this.startSizing(e, handle);
32252         }
32253     },
32254
32255     // private
32256     onMouseUp : function(e){
32257         var size = this.resizeElement();
32258         this.resizing = false;
32259         this.handleOut();
32260         this.overlay.hide();
32261         this.proxy.hide();
32262         this.fireEvent("resize", this, size.width, size.height, e);
32263     },
32264
32265     // private
32266     updateChildSize : function(){
32267         
32268         if(this.resizeChild){
32269             var el = this.el;
32270             var child = this.resizeChild;
32271             var adj = this.adjustments;
32272             if(el.dom.offsetWidth){
32273                 var b = el.getSize(true);
32274                 child.setSize(b.width+adj[0], b.height+adj[1]);
32275             }
32276             // Second call here for IE
32277             // The first call enables instant resizing and
32278             // the second call corrects scroll bars if they
32279             // exist
32280             if(Roo.isIE){
32281                 setTimeout(function(){
32282                     if(el.dom.offsetWidth){
32283                         var b = el.getSize(true);
32284                         child.setSize(b.width+adj[0], b.height+adj[1]);
32285                     }
32286                 }, 10);
32287             }
32288         }
32289     },
32290
32291     // private
32292     snap : function(value, inc, min){
32293         if(!inc || !value) {
32294             return value;
32295         }
32296         var newValue = value;
32297         var m = value % inc;
32298         if(m > 0){
32299             if(m > (inc/2)){
32300                 newValue = value + (inc-m);
32301             }else{
32302                 newValue = value - m;
32303             }
32304         }
32305         return Math.max(min, newValue);
32306     },
32307
32308     // private
32309     resizeElement : function(){
32310         var box = this.proxy.getBox();
32311         if(this.updateBox){
32312             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32313         }else{
32314             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32315         }
32316         this.updateChildSize();
32317         if(!this.dynamic){
32318             this.proxy.hide();
32319         }
32320         return box;
32321     },
32322
32323     // private
32324     constrain : function(v, diff, m, mx){
32325         if(v - diff < m){
32326             diff = v - m;
32327         }else if(v - diff > mx){
32328             diff = mx - v;
32329         }
32330         return diff;
32331     },
32332
32333     // private
32334     onMouseMove : function(e){
32335         
32336         if(this.enabled){
32337             try{// try catch so if something goes wrong the user doesn't get hung
32338
32339             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32340                 return;
32341             }
32342
32343             //var curXY = this.startPoint;
32344             var curSize = this.curSize || this.startBox;
32345             var x = this.startBox.x, y = this.startBox.y;
32346             var ox = x, oy = y;
32347             var w = curSize.width, h = curSize.height;
32348             var ow = w, oh = h;
32349             var mw = this.minWidth, mh = this.minHeight;
32350             var mxw = this.maxWidth, mxh = this.maxHeight;
32351             var wi = this.widthIncrement;
32352             var hi = this.heightIncrement;
32353
32354             var eventXY = e.getXY();
32355             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32356             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32357
32358             var pos = this.activeHandle.position;
32359
32360             switch(pos){
32361                 case "east":
32362                     w += diffX;
32363                     w = Math.min(Math.max(mw, w), mxw);
32364                     break;
32365              
32366                 case "south":
32367                     h += diffY;
32368                     h = Math.min(Math.max(mh, h), mxh);
32369                     break;
32370                 case "southeast":
32371                     w += diffX;
32372                     h += diffY;
32373                     w = Math.min(Math.max(mw, w), mxw);
32374                     h = Math.min(Math.max(mh, h), mxh);
32375                     break;
32376                 case "north":
32377                     diffY = this.constrain(h, diffY, mh, mxh);
32378                     y += diffY;
32379                     h -= diffY;
32380                     break;
32381                 case "hdrag":
32382                     
32383                     if (wi) {
32384                         var adiffX = Math.abs(diffX);
32385                         var sub = (adiffX % wi); // how much 
32386                         if (sub > (wi/2)) { // far enough to snap
32387                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32388                         } else {
32389                             // remove difference.. 
32390                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32391                         }
32392                     }
32393                     x += diffX;
32394                     x = Math.max(this.minX, x);
32395                     break;
32396                 case "west":
32397                     diffX = this.constrain(w, diffX, mw, mxw);
32398                     x += diffX;
32399                     w -= diffX;
32400                     break;
32401                 case "northeast":
32402                     w += diffX;
32403                     w = Math.min(Math.max(mw, w), mxw);
32404                     diffY = this.constrain(h, diffY, mh, mxh);
32405                     y += diffY;
32406                     h -= diffY;
32407                     break;
32408                 case "northwest":
32409                     diffX = this.constrain(w, diffX, mw, mxw);
32410                     diffY = this.constrain(h, diffY, mh, mxh);
32411                     y += diffY;
32412                     h -= diffY;
32413                     x += diffX;
32414                     w -= diffX;
32415                     break;
32416                case "southwest":
32417                     diffX = this.constrain(w, diffX, mw, mxw);
32418                     h += diffY;
32419                     h = Math.min(Math.max(mh, h), mxh);
32420                     x += diffX;
32421                     w -= diffX;
32422                     break;
32423             }
32424
32425             var sw = this.snap(w, wi, mw);
32426             var sh = this.snap(h, hi, mh);
32427             if(sw != w || sh != h){
32428                 switch(pos){
32429                     case "northeast":
32430                         y -= sh - h;
32431                     break;
32432                     case "north":
32433                         y -= sh - h;
32434                         break;
32435                     case "southwest":
32436                         x -= sw - w;
32437                     break;
32438                     case "west":
32439                         x -= sw - w;
32440                         break;
32441                     case "northwest":
32442                         x -= sw - w;
32443                         y -= sh - h;
32444                     break;
32445                 }
32446                 w = sw;
32447                 h = sh;
32448             }
32449
32450             if(this.preserveRatio){
32451                 switch(pos){
32452                     case "southeast":
32453                     case "east":
32454                         h = oh * (w/ow);
32455                         h = Math.min(Math.max(mh, h), mxh);
32456                         w = ow * (h/oh);
32457                        break;
32458                     case "south":
32459                         w = ow * (h/oh);
32460                         w = Math.min(Math.max(mw, w), mxw);
32461                         h = oh * (w/ow);
32462                         break;
32463                     case "northeast":
32464                         w = ow * (h/oh);
32465                         w = Math.min(Math.max(mw, w), mxw);
32466                         h = oh * (w/ow);
32467                     break;
32468                     case "north":
32469                         var tw = w;
32470                         w = ow * (h/oh);
32471                         w = Math.min(Math.max(mw, w), mxw);
32472                         h = oh * (w/ow);
32473                         x += (tw - w) / 2;
32474                         break;
32475                     case "southwest":
32476                         h = oh * (w/ow);
32477                         h = Math.min(Math.max(mh, h), mxh);
32478                         var tw = w;
32479                         w = ow * (h/oh);
32480                         x += tw - w;
32481                         break;
32482                     case "west":
32483                         var th = h;
32484                         h = oh * (w/ow);
32485                         h = Math.min(Math.max(mh, h), mxh);
32486                         y += (th - h) / 2;
32487                         var tw = w;
32488                         w = ow * (h/oh);
32489                         x += tw - w;
32490                        break;
32491                     case "northwest":
32492                         var tw = w;
32493                         var th = h;
32494                         h = oh * (w/ow);
32495                         h = Math.min(Math.max(mh, h), mxh);
32496                         w = ow * (h/oh);
32497                         y += th - h;
32498                         x += tw - w;
32499                        break;
32500
32501                 }
32502             }
32503             if (pos == 'hdrag') {
32504                 w = ow;
32505             }
32506             this.proxy.setBounds(x, y, w, h);
32507             if(this.dynamic){
32508                 this.resizeElement();
32509             }
32510             }catch(e){}
32511         }
32512         this.fireEvent("resizing", this, x, y, w, h, e);
32513     },
32514
32515     // private
32516     handleOver : function(){
32517         if(this.enabled){
32518             this.el.addClass("x-resizable-over");
32519         }
32520     },
32521
32522     // private
32523     handleOut : function(){
32524         if(!this.resizing){
32525             this.el.removeClass("x-resizable-over");
32526         }
32527     },
32528
32529     /**
32530      * Returns the element this component is bound to.
32531      * @return {Roo.Element}
32532      */
32533     getEl : function(){
32534         return this.el;
32535     },
32536
32537     /**
32538      * Returns the resizeChild element (or null).
32539      * @return {Roo.Element}
32540      */
32541     getResizeChild : function(){
32542         return this.resizeChild;
32543     },
32544     groupHandler : function()
32545     {
32546         
32547     },
32548     /**
32549      * Destroys this resizable. If the element was wrapped and
32550      * removeEl is not true then the element remains.
32551      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32552      */
32553     destroy : function(removeEl){
32554         this.proxy.remove();
32555         if(this.overlay){
32556             this.overlay.removeAllListeners();
32557             this.overlay.remove();
32558         }
32559         var ps = Roo.Resizable.positions;
32560         for(var k in ps){
32561             if(typeof ps[k] != "function" && this[ps[k]]){
32562                 var h = this[ps[k]];
32563                 h.el.removeAllListeners();
32564                 h.el.remove();
32565             }
32566         }
32567         if(removeEl){
32568             this.el.update("");
32569             this.el.remove();
32570         }
32571     }
32572 });
32573
32574 // private
32575 // hash to map config positions to true positions
32576 Roo.Resizable.positions = {
32577     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32578     hd: "hdrag"
32579 };
32580
32581 // private
32582 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32583     if(!this.tpl){
32584         // only initialize the template if resizable is used
32585         var tpl = Roo.DomHelper.createTemplate(
32586             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32587         );
32588         tpl.compile();
32589         Roo.Resizable.Handle.prototype.tpl = tpl;
32590     }
32591     this.position = pos;
32592     this.rz = rz;
32593     // show north drag fro topdra
32594     var handlepos = pos == 'hdrag' ? 'north' : pos;
32595     
32596     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32597     if (pos == 'hdrag') {
32598         this.el.setStyle('cursor', 'pointer');
32599     }
32600     this.el.unselectable();
32601     if(transparent){
32602         this.el.setOpacity(0);
32603     }
32604     this.el.on("mousedown", this.onMouseDown, this);
32605     if(!disableTrackOver){
32606         this.el.on("mouseover", this.onMouseOver, this);
32607         this.el.on("mouseout", this.onMouseOut, this);
32608     }
32609 };
32610
32611 // private
32612 Roo.Resizable.Handle.prototype = {
32613     afterResize : function(rz){
32614         Roo.log('after?');
32615         // do nothing
32616     },
32617     // private
32618     onMouseDown : function(e){
32619         this.rz.onMouseDown(this, e);
32620     },
32621     // private
32622     onMouseOver : function(e){
32623         this.rz.handleOver(this, e);
32624     },
32625     // private
32626     onMouseOut : function(e){
32627         this.rz.handleOut(this, e);
32628     }
32629 };/*
32630  * Based on:
32631  * Ext JS Library 1.1.1
32632  * Copyright(c) 2006-2007, Ext JS, LLC.
32633  *
32634  * Originally Released Under LGPL - original licence link has changed is not relivant.
32635  *
32636  * Fork - LGPL
32637  * <script type="text/javascript">
32638  */
32639
32640 /**
32641  * @class Roo.Editor
32642  * @extends Roo.Component
32643  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32644  * @constructor
32645  * Create a new Editor
32646  * @param {Roo.form.Field} field The Field object (or descendant)
32647  * @param {Object} config The config object
32648  */
32649 Roo.Editor = function(field, config){
32650     Roo.Editor.superclass.constructor.call(this, config);
32651     this.field = field;
32652     this.addEvents({
32653         /**
32654              * @event beforestartedit
32655              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32656              * false from the handler of this event.
32657              * @param {Editor} this
32658              * @param {Roo.Element} boundEl The underlying element bound to this editor
32659              * @param {Mixed} value The field value being set
32660              */
32661         "beforestartedit" : true,
32662         /**
32663              * @event startedit
32664              * Fires when this editor is displayed
32665              * @param {Roo.Element} boundEl The underlying element bound to this editor
32666              * @param {Mixed} value The starting field value
32667              */
32668         "startedit" : true,
32669         /**
32670              * @event beforecomplete
32671              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32672              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32673              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32674              * event will not fire since no edit actually occurred.
32675              * @param {Editor} this
32676              * @param {Mixed} value The current field value
32677              * @param {Mixed} startValue The original field value
32678              */
32679         "beforecomplete" : true,
32680         /**
32681              * @event complete
32682              * Fires after editing is complete and any changed value has been written to the underlying field.
32683              * @param {Editor} this
32684              * @param {Mixed} value The current field value
32685              * @param {Mixed} startValue The original field value
32686              */
32687         "complete" : true,
32688         /**
32689          * @event specialkey
32690          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32691          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32692          * @param {Roo.form.Field} this
32693          * @param {Roo.EventObject} e The event object
32694          */
32695         "specialkey" : true
32696     });
32697 };
32698
32699 Roo.extend(Roo.Editor, Roo.Component, {
32700     /**
32701      * @cfg {Boolean/String} autosize
32702      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32703      * or "height" to adopt the height only (defaults to false)
32704      */
32705     /**
32706      * @cfg {Boolean} revertInvalid
32707      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32708      * validation fails (defaults to true)
32709      */
32710     /**
32711      * @cfg {Boolean} ignoreNoChange
32712      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32713      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32714      * will never be ignored.
32715      */
32716     /**
32717      * @cfg {Boolean} hideEl
32718      * False to keep the bound element visible while the editor is displayed (defaults to true)
32719      */
32720     /**
32721      * @cfg {Mixed} value
32722      * The data value of the underlying field (defaults to "")
32723      */
32724     value : "",
32725     /**
32726      * @cfg {String} alignment
32727      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32728      */
32729     alignment: "c-c?",
32730     /**
32731      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32732      * for bottom-right shadow (defaults to "frame")
32733      */
32734     shadow : "frame",
32735     /**
32736      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32737      */
32738     constrain : false,
32739     /**
32740      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32741      */
32742     completeOnEnter : false,
32743     /**
32744      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32745      */
32746     cancelOnEsc : false,
32747     /**
32748      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32749      */
32750     updateEl : false,
32751
32752     // private
32753     onRender : function(ct, position){
32754         this.el = new Roo.Layer({
32755             shadow: this.shadow,
32756             cls: "x-editor",
32757             parentEl : ct,
32758             shim : this.shim,
32759             shadowOffset:4,
32760             id: this.id,
32761             constrain: this.constrain
32762         });
32763         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32764         if(this.field.msgTarget != 'title'){
32765             this.field.msgTarget = 'qtip';
32766         }
32767         this.field.render(this.el);
32768         if(Roo.isGecko){
32769             this.field.el.dom.setAttribute('autocomplete', 'off');
32770         }
32771         this.field.on("specialkey", this.onSpecialKey, this);
32772         if(this.swallowKeys){
32773             this.field.el.swallowEvent(['keydown','keypress']);
32774         }
32775         this.field.show();
32776         this.field.on("blur", this.onBlur, this);
32777         if(this.field.grow){
32778             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32779         }
32780     },
32781
32782     onSpecialKey : function(field, e)
32783     {
32784         //Roo.log('editor onSpecialKey');
32785         if(this.completeOnEnter && e.getKey() == e.ENTER){
32786             e.stopEvent();
32787             this.completeEdit();
32788             return;
32789         }
32790         // do not fire special key otherwise it might hide close the editor...
32791         if(e.getKey() == e.ENTER){    
32792             return;
32793         }
32794         if(this.cancelOnEsc && e.getKey() == e.ESC){
32795             this.cancelEdit();
32796             return;
32797         } 
32798         this.fireEvent('specialkey', field, e);
32799     
32800     },
32801
32802     /**
32803      * Starts the editing process and shows the editor.
32804      * @param {String/HTMLElement/Element} el The element to edit
32805      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32806       * to the innerHTML of el.
32807      */
32808     startEdit : function(el, value){
32809         if(this.editing){
32810             this.completeEdit();
32811         }
32812         this.boundEl = Roo.get(el);
32813         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32814         if(!this.rendered){
32815             this.render(this.parentEl || document.body);
32816         }
32817         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32818             return;
32819         }
32820         this.startValue = v;
32821         this.field.setValue(v);
32822         if(this.autoSize){
32823             var sz = this.boundEl.getSize();
32824             switch(this.autoSize){
32825                 case "width":
32826                 this.setSize(sz.width,  "");
32827                 break;
32828                 case "height":
32829                 this.setSize("",  sz.height);
32830                 break;
32831                 default:
32832                 this.setSize(sz.width,  sz.height);
32833             }
32834         }
32835         this.el.alignTo(this.boundEl, this.alignment);
32836         this.editing = true;
32837         if(Roo.QuickTips){
32838             Roo.QuickTips.disable();
32839         }
32840         this.show();
32841     },
32842
32843     /**
32844      * Sets the height and width of this editor.
32845      * @param {Number} width The new width
32846      * @param {Number} height The new height
32847      */
32848     setSize : function(w, h){
32849         this.field.setSize(w, h);
32850         if(this.el){
32851             this.el.sync();
32852         }
32853     },
32854
32855     /**
32856      * Realigns the editor to the bound field based on the current alignment config value.
32857      */
32858     realign : function(){
32859         this.el.alignTo(this.boundEl, this.alignment);
32860     },
32861
32862     /**
32863      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32864      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32865      */
32866     completeEdit : function(remainVisible){
32867         if(!this.editing){
32868             return;
32869         }
32870         var v = this.getValue();
32871         if(this.revertInvalid !== false && !this.field.isValid()){
32872             v = this.startValue;
32873             this.cancelEdit(true);
32874         }
32875         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32876             this.editing = false;
32877             this.hide();
32878             return;
32879         }
32880         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32881             this.editing = false;
32882             if(this.updateEl && this.boundEl){
32883                 this.boundEl.update(v);
32884             }
32885             if(remainVisible !== true){
32886                 this.hide();
32887             }
32888             this.fireEvent("complete", this, v, this.startValue);
32889         }
32890     },
32891
32892     // private
32893     onShow : function(){
32894         this.el.show();
32895         if(this.hideEl !== false){
32896             this.boundEl.hide();
32897         }
32898         this.field.show();
32899         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32900             this.fixIEFocus = true;
32901             this.deferredFocus.defer(50, this);
32902         }else{
32903             this.field.focus();
32904         }
32905         this.fireEvent("startedit", this.boundEl, this.startValue);
32906     },
32907
32908     deferredFocus : function(){
32909         if(this.editing){
32910             this.field.focus();
32911         }
32912     },
32913
32914     /**
32915      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32916      * reverted to the original starting value.
32917      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32918      * cancel (defaults to false)
32919      */
32920     cancelEdit : function(remainVisible){
32921         if(this.editing){
32922             this.setValue(this.startValue);
32923             if(remainVisible !== true){
32924                 this.hide();
32925             }
32926         }
32927     },
32928
32929     // private
32930     onBlur : function(){
32931         if(this.allowBlur !== true && this.editing){
32932             this.completeEdit();
32933         }
32934     },
32935
32936     // private
32937     onHide : function(){
32938         if(this.editing){
32939             this.completeEdit();
32940             return;
32941         }
32942         this.field.blur();
32943         if(this.field.collapse){
32944             this.field.collapse();
32945         }
32946         this.el.hide();
32947         if(this.hideEl !== false){
32948             this.boundEl.show();
32949         }
32950         if(Roo.QuickTips){
32951             Roo.QuickTips.enable();
32952         }
32953     },
32954
32955     /**
32956      * Sets the data value of the editor
32957      * @param {Mixed} value Any valid value supported by the underlying field
32958      */
32959     setValue : function(v){
32960         this.field.setValue(v);
32961     },
32962
32963     /**
32964      * Gets the data value of the editor
32965      * @return {Mixed} The data value
32966      */
32967     getValue : function(){
32968         return this.field.getValue();
32969     }
32970 });/*
32971  * Based on:
32972  * Ext JS Library 1.1.1
32973  * Copyright(c) 2006-2007, Ext JS, LLC.
32974  *
32975  * Originally Released Under LGPL - original licence link has changed is not relivant.
32976  *
32977  * Fork - LGPL
32978  * <script type="text/javascript">
32979  */
32980  
32981 /**
32982  * @class Roo.BasicDialog
32983  * @extends Roo.util.Observable
32984  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32985  * <pre><code>
32986 var dlg = new Roo.BasicDialog("my-dlg", {
32987     height: 200,
32988     width: 300,
32989     minHeight: 100,
32990     minWidth: 150,
32991     modal: true,
32992     proxyDrag: true,
32993     shadow: true
32994 });
32995 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32996 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32997 dlg.addButton('Cancel', dlg.hide, dlg);
32998 dlg.show();
32999 </code></pre>
33000   <b>A Dialog should always be a direct child of the body element.</b>
33001  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33002  * @cfg {String} title Default text to display in the title bar (defaults to null)
33003  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33004  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33005  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33006  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33007  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33008  * (defaults to null with no animation)
33009  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33010  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33011  * property for valid values (defaults to 'all')
33012  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33013  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33014  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33015  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33016  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33017  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33018  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33019  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33020  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33021  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33022  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33023  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33024  * draggable = true (defaults to false)
33025  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33026  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33027  * shadow (defaults to false)
33028  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33029  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33030  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33031  * @cfg {Array} buttons Array of buttons
33032  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33033  * @constructor
33034  * Create a new BasicDialog.
33035  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33036  * @param {Object} config Configuration options
33037  */
33038 Roo.BasicDialog = function(el, config){
33039     this.el = Roo.get(el);
33040     var dh = Roo.DomHelper;
33041     if(!this.el && config && config.autoCreate){
33042         if(typeof config.autoCreate == "object"){
33043             if(!config.autoCreate.id){
33044                 config.autoCreate.id = el;
33045             }
33046             this.el = dh.append(document.body,
33047                         config.autoCreate, true);
33048         }else{
33049             this.el = dh.append(document.body,
33050                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33051         }
33052     }
33053     el = this.el;
33054     el.setDisplayed(true);
33055     el.hide = this.hideAction;
33056     this.id = el.id;
33057     el.addClass("x-dlg");
33058
33059     Roo.apply(this, config);
33060
33061     this.proxy = el.createProxy("x-dlg-proxy");
33062     this.proxy.hide = this.hideAction;
33063     this.proxy.setOpacity(.5);
33064     this.proxy.hide();
33065
33066     if(config.width){
33067         el.setWidth(config.width);
33068     }
33069     if(config.height){
33070         el.setHeight(config.height);
33071     }
33072     this.size = el.getSize();
33073     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33074         this.xy = [config.x,config.y];
33075     }else{
33076         this.xy = el.getCenterXY(true);
33077     }
33078     /** The header element @type Roo.Element */
33079     this.header = el.child("> .x-dlg-hd");
33080     /** The body element @type Roo.Element */
33081     this.body = el.child("> .x-dlg-bd");
33082     /** The footer element @type Roo.Element */
33083     this.footer = el.child("> .x-dlg-ft");
33084
33085     if(!this.header){
33086         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33087     }
33088     if(!this.body){
33089         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33090     }
33091
33092     this.header.unselectable();
33093     if(this.title){
33094         this.header.update(this.title);
33095     }
33096     // this element allows the dialog to be focused for keyboard event
33097     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33098     this.focusEl.swallowEvent("click", true);
33099
33100     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33101
33102     // wrap the body and footer for special rendering
33103     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33104     if(this.footer){
33105         this.bwrap.dom.appendChild(this.footer.dom);
33106     }
33107
33108     this.bg = this.el.createChild({
33109         tag: "div", cls:"x-dlg-bg",
33110         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33111     });
33112     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33113
33114
33115     if(this.autoScroll !== false && !this.autoTabs){
33116         this.body.setStyle("overflow", "auto");
33117     }
33118
33119     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33120
33121     if(this.closable !== false){
33122         this.el.addClass("x-dlg-closable");
33123         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33124         this.close.on("click", this.closeClick, this);
33125         this.close.addClassOnOver("x-dlg-close-over");
33126     }
33127     if(this.collapsible !== false){
33128         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33129         this.collapseBtn.on("click", this.collapseClick, this);
33130         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33131         this.header.on("dblclick", this.collapseClick, this);
33132     }
33133     if(this.resizable !== false){
33134         this.el.addClass("x-dlg-resizable");
33135         this.resizer = new Roo.Resizable(el, {
33136             minWidth: this.minWidth || 80,
33137             minHeight:this.minHeight || 80,
33138             handles: this.resizeHandles || "all",
33139             pinned: true
33140         });
33141         this.resizer.on("beforeresize", this.beforeResize, this);
33142         this.resizer.on("resize", this.onResize, this);
33143     }
33144     if(this.draggable !== false){
33145         el.addClass("x-dlg-draggable");
33146         if (!this.proxyDrag) {
33147             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33148         }
33149         else {
33150             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33151         }
33152         dd.setHandleElId(this.header.id);
33153         dd.endDrag = this.endMove.createDelegate(this);
33154         dd.startDrag = this.startMove.createDelegate(this);
33155         dd.onDrag = this.onDrag.createDelegate(this);
33156         dd.scroll = false;
33157         this.dd = dd;
33158     }
33159     if(this.modal){
33160         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33161         this.mask.enableDisplayMode("block");
33162         this.mask.hide();
33163         this.el.addClass("x-dlg-modal");
33164     }
33165     if(this.shadow){
33166         this.shadow = new Roo.Shadow({
33167             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33168             offset : this.shadowOffset
33169         });
33170     }else{
33171         this.shadowOffset = 0;
33172     }
33173     if(Roo.useShims && this.shim !== false){
33174         this.shim = this.el.createShim();
33175         this.shim.hide = this.hideAction;
33176         this.shim.hide();
33177     }else{
33178         this.shim = false;
33179     }
33180     if(this.autoTabs){
33181         this.initTabs();
33182     }
33183     if (this.buttons) { 
33184         var bts= this.buttons;
33185         this.buttons = [];
33186         Roo.each(bts, function(b) {
33187             this.addButton(b);
33188         }, this);
33189     }
33190     
33191     
33192     this.addEvents({
33193         /**
33194          * @event keydown
33195          * Fires when a key is pressed
33196          * @param {Roo.BasicDialog} this
33197          * @param {Roo.EventObject} e
33198          */
33199         "keydown" : true,
33200         /**
33201          * @event move
33202          * Fires when this dialog is moved by the user.
33203          * @param {Roo.BasicDialog} this
33204          * @param {Number} x The new page X
33205          * @param {Number} y The new page Y
33206          */
33207         "move" : true,
33208         /**
33209          * @event resize
33210          * Fires when this dialog is resized by the user.
33211          * @param {Roo.BasicDialog} this
33212          * @param {Number} width The new width
33213          * @param {Number} height The new height
33214          */
33215         "resize" : true,
33216         /**
33217          * @event beforehide
33218          * Fires before this dialog is hidden.
33219          * @param {Roo.BasicDialog} this
33220          */
33221         "beforehide" : true,
33222         /**
33223          * @event hide
33224          * Fires when this dialog is hidden.
33225          * @param {Roo.BasicDialog} this
33226          */
33227         "hide" : true,
33228         /**
33229          * @event beforeshow
33230          * Fires before this dialog is shown.
33231          * @param {Roo.BasicDialog} this
33232          */
33233         "beforeshow" : true,
33234         /**
33235          * @event show
33236          * Fires when this dialog is shown.
33237          * @param {Roo.BasicDialog} this
33238          */
33239         "show" : true
33240     });
33241     el.on("keydown", this.onKeyDown, this);
33242     el.on("mousedown", this.toFront, this);
33243     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33244     this.el.hide();
33245     Roo.DialogManager.register(this);
33246     Roo.BasicDialog.superclass.constructor.call(this);
33247 };
33248
33249 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33250     shadowOffset: Roo.isIE ? 6 : 5,
33251     minHeight: 80,
33252     minWidth: 200,
33253     minButtonWidth: 75,
33254     defaultButton: null,
33255     buttonAlign: "right",
33256     tabTag: 'div',
33257     firstShow: true,
33258
33259     /**
33260      * Sets the dialog title text
33261      * @param {String} text The title text to display
33262      * @return {Roo.BasicDialog} this
33263      */
33264     setTitle : function(text){
33265         this.header.update(text);
33266         return this;
33267     },
33268
33269     // private
33270     closeClick : function(){
33271         this.hide();
33272     },
33273
33274     // private
33275     collapseClick : function(){
33276         this[this.collapsed ? "expand" : "collapse"]();
33277     },
33278
33279     /**
33280      * Collapses the dialog to its minimized state (only the title bar is visible).
33281      * Equivalent to the user clicking the collapse dialog button.
33282      */
33283     collapse : function(){
33284         if(!this.collapsed){
33285             this.collapsed = true;
33286             this.el.addClass("x-dlg-collapsed");
33287             this.restoreHeight = this.el.getHeight();
33288             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33289         }
33290     },
33291
33292     /**
33293      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33294      * clicking the expand dialog button.
33295      */
33296     expand : function(){
33297         if(this.collapsed){
33298             this.collapsed = false;
33299             this.el.removeClass("x-dlg-collapsed");
33300             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33301         }
33302     },
33303
33304     /**
33305      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33306      * @return {Roo.TabPanel} The tabs component
33307      */
33308     initTabs : function(){
33309         var tabs = this.getTabs();
33310         while(tabs.getTab(0)){
33311             tabs.removeTab(0);
33312         }
33313         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33314             var dom = el.dom;
33315             tabs.addTab(Roo.id(dom), dom.title);
33316             dom.title = "";
33317         });
33318         tabs.activate(0);
33319         return tabs;
33320     },
33321
33322     // private
33323     beforeResize : function(){
33324         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33325     },
33326
33327     // private
33328     onResize : function(){
33329         this.refreshSize();
33330         this.syncBodyHeight();
33331         this.adjustAssets();
33332         this.focus();
33333         this.fireEvent("resize", this, this.size.width, this.size.height);
33334     },
33335
33336     // private
33337     onKeyDown : function(e){
33338         if(this.isVisible()){
33339             this.fireEvent("keydown", this, e);
33340         }
33341     },
33342
33343     /**
33344      * Resizes the dialog.
33345      * @param {Number} width
33346      * @param {Number} height
33347      * @return {Roo.BasicDialog} this
33348      */
33349     resizeTo : function(width, height){
33350         this.el.setSize(width, height);
33351         this.size = {width: width, height: height};
33352         this.syncBodyHeight();
33353         if(this.fixedcenter){
33354             this.center();
33355         }
33356         if(this.isVisible()){
33357             this.constrainXY();
33358             this.adjustAssets();
33359         }
33360         this.fireEvent("resize", this, width, height);
33361         return this;
33362     },
33363
33364
33365     /**
33366      * Resizes the dialog to fit the specified content size.
33367      * @param {Number} width
33368      * @param {Number} height
33369      * @return {Roo.BasicDialog} this
33370      */
33371     setContentSize : function(w, h){
33372         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33373         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33374         //if(!this.el.isBorderBox()){
33375             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33376             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33377         //}
33378         if(this.tabs){
33379             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33380             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33381         }
33382         this.resizeTo(w, h);
33383         return this;
33384     },
33385
33386     /**
33387      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33388      * executed in response to a particular key being pressed while the dialog is active.
33389      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33390      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33391      * @param {Function} fn The function to call
33392      * @param {Object} scope (optional) The scope of the function
33393      * @return {Roo.BasicDialog} this
33394      */
33395     addKeyListener : function(key, fn, scope){
33396         var keyCode, shift, ctrl, alt;
33397         if(typeof key == "object" && !(key instanceof Array)){
33398             keyCode = key["key"];
33399             shift = key["shift"];
33400             ctrl = key["ctrl"];
33401             alt = key["alt"];
33402         }else{
33403             keyCode = key;
33404         }
33405         var handler = function(dlg, e){
33406             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33407                 var k = e.getKey();
33408                 if(keyCode instanceof Array){
33409                     for(var i = 0, len = keyCode.length; i < len; i++){
33410                         if(keyCode[i] == k){
33411                           fn.call(scope || window, dlg, k, e);
33412                           return;
33413                         }
33414                     }
33415                 }else{
33416                     if(k == keyCode){
33417                         fn.call(scope || window, dlg, k, e);
33418                     }
33419                 }
33420             }
33421         };
33422         this.on("keydown", handler);
33423         return this;
33424     },
33425
33426     /**
33427      * Returns the TabPanel component (creates it if it doesn't exist).
33428      * Note: If you wish to simply check for the existence of tabs without creating them,
33429      * check for a null 'tabs' property.
33430      * @return {Roo.TabPanel} The tabs component
33431      */
33432     getTabs : function(){
33433         if(!this.tabs){
33434             this.el.addClass("x-dlg-auto-tabs");
33435             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33436             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33437         }
33438         return this.tabs;
33439     },
33440
33441     /**
33442      * Adds a button to the footer section of the dialog.
33443      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33444      * object or a valid Roo.DomHelper element config
33445      * @param {Function} handler The function called when the button is clicked
33446      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33447      * @return {Roo.Button} The new button
33448      */
33449     addButton : function(config, handler, scope){
33450         var dh = Roo.DomHelper;
33451         if(!this.footer){
33452             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33453         }
33454         if(!this.btnContainer){
33455             var tb = this.footer.createChild({
33456
33457                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33458                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33459             }, null, true);
33460             this.btnContainer = tb.firstChild.firstChild.firstChild;
33461         }
33462         var bconfig = {
33463             handler: handler,
33464             scope: scope,
33465             minWidth: this.minButtonWidth,
33466             hideParent:true
33467         };
33468         if(typeof config == "string"){
33469             bconfig.text = config;
33470         }else{
33471             if(config.tag){
33472                 bconfig.dhconfig = config;
33473             }else{
33474                 Roo.apply(bconfig, config);
33475             }
33476         }
33477         var fc = false;
33478         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33479             bconfig.position = Math.max(0, bconfig.position);
33480             fc = this.btnContainer.childNodes[bconfig.position];
33481         }
33482          
33483         var btn = new Roo.Button(
33484             fc ? 
33485                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33486                 : this.btnContainer.appendChild(document.createElement("td")),
33487             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33488             bconfig
33489         );
33490         this.syncBodyHeight();
33491         if(!this.buttons){
33492             /**
33493              * Array of all the buttons that have been added to this dialog via addButton
33494              * @type Array
33495              */
33496             this.buttons = [];
33497         }
33498         this.buttons.push(btn);
33499         return btn;
33500     },
33501
33502     /**
33503      * Sets the default button to be focused when the dialog is displayed.
33504      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33505      * @return {Roo.BasicDialog} this
33506      */
33507     setDefaultButton : function(btn){
33508         this.defaultButton = btn;
33509         return this;
33510     },
33511
33512     // private
33513     getHeaderFooterHeight : function(safe){
33514         var height = 0;
33515         if(this.header){
33516            height += this.header.getHeight();
33517         }
33518         if(this.footer){
33519            var fm = this.footer.getMargins();
33520             height += (this.footer.getHeight()+fm.top+fm.bottom);
33521         }
33522         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33523         height += this.centerBg.getPadding("tb");
33524         return height;
33525     },
33526
33527     // private
33528     syncBodyHeight : function()
33529     {
33530         var bd = this.body, // the text
33531             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33532             bw = this.bwrap;
33533         var height = this.size.height - this.getHeaderFooterHeight(false);
33534         bd.setHeight(height-bd.getMargins("tb"));
33535         var hh = this.header.getHeight();
33536         var h = this.size.height-hh;
33537         cb.setHeight(h);
33538         
33539         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33540         bw.setHeight(h-cb.getPadding("tb"));
33541         
33542         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33543         bd.setWidth(bw.getWidth(true));
33544         if(this.tabs){
33545             this.tabs.syncHeight();
33546             if(Roo.isIE){
33547                 this.tabs.el.repaint();
33548             }
33549         }
33550     },
33551
33552     /**
33553      * Restores the previous state of the dialog if Roo.state is configured.
33554      * @return {Roo.BasicDialog} this
33555      */
33556     restoreState : function(){
33557         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33558         if(box && box.width){
33559             this.xy = [box.x, box.y];
33560             this.resizeTo(box.width, box.height);
33561         }
33562         return this;
33563     },
33564
33565     // private
33566     beforeShow : function(){
33567         this.expand();
33568         if(this.fixedcenter){
33569             this.xy = this.el.getCenterXY(true);
33570         }
33571         if(this.modal){
33572             Roo.get(document.body).addClass("x-body-masked");
33573             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33574             this.mask.show();
33575         }
33576         this.constrainXY();
33577     },
33578
33579     // private
33580     animShow : function(){
33581         var b = Roo.get(this.animateTarget).getBox();
33582         this.proxy.setSize(b.width, b.height);
33583         this.proxy.setLocation(b.x, b.y);
33584         this.proxy.show();
33585         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33586                     true, .35, this.showEl.createDelegate(this));
33587     },
33588
33589     /**
33590      * Shows the dialog.
33591      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33592      * @return {Roo.BasicDialog} this
33593      */
33594     show : function(animateTarget){
33595         if (this.fireEvent("beforeshow", this) === false){
33596             return;
33597         }
33598         if(this.syncHeightBeforeShow){
33599             this.syncBodyHeight();
33600         }else if(this.firstShow){
33601             this.firstShow = false;
33602             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33603         }
33604         this.animateTarget = animateTarget || this.animateTarget;
33605         if(!this.el.isVisible()){
33606             this.beforeShow();
33607             if(this.animateTarget && Roo.get(this.animateTarget)){
33608                 this.animShow();
33609             }else{
33610                 this.showEl();
33611             }
33612         }
33613         return this;
33614     },
33615
33616     // private
33617     showEl : function(){
33618         this.proxy.hide();
33619         this.el.setXY(this.xy);
33620         this.el.show();
33621         this.adjustAssets(true);
33622         this.toFront();
33623         this.focus();
33624         // IE peekaboo bug - fix found by Dave Fenwick
33625         if(Roo.isIE){
33626             this.el.repaint();
33627         }
33628         this.fireEvent("show", this);
33629     },
33630
33631     /**
33632      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33633      * dialog itself will receive focus.
33634      */
33635     focus : function(){
33636         if(this.defaultButton){
33637             this.defaultButton.focus();
33638         }else{
33639             this.focusEl.focus();
33640         }
33641     },
33642
33643     // private
33644     constrainXY : function(){
33645         if(this.constraintoviewport !== false){
33646             if(!this.viewSize){
33647                 if(this.container){
33648                     var s = this.container.getSize();
33649                     this.viewSize = [s.width, s.height];
33650                 }else{
33651                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33652                 }
33653             }
33654             var s = Roo.get(this.container||document).getScroll();
33655
33656             var x = this.xy[0], y = this.xy[1];
33657             var w = this.size.width, h = this.size.height;
33658             var vw = this.viewSize[0], vh = this.viewSize[1];
33659             // only move it if it needs it
33660             var moved = false;
33661             // first validate right/bottom
33662             if(x + w > vw+s.left){
33663                 x = vw - w;
33664                 moved = true;
33665             }
33666             if(y + h > vh+s.top){
33667                 y = vh - h;
33668                 moved = true;
33669             }
33670             // then make sure top/left isn't negative
33671             if(x < s.left){
33672                 x = s.left;
33673                 moved = true;
33674             }
33675             if(y < s.top){
33676                 y = s.top;
33677                 moved = true;
33678             }
33679             if(moved){
33680                 // cache xy
33681                 this.xy = [x, y];
33682                 if(this.isVisible()){
33683                     this.el.setLocation(x, y);
33684                     this.adjustAssets();
33685                 }
33686             }
33687         }
33688     },
33689
33690     // private
33691     onDrag : function(){
33692         if(!this.proxyDrag){
33693             this.xy = this.el.getXY();
33694             this.adjustAssets();
33695         }
33696     },
33697
33698     // private
33699     adjustAssets : function(doShow){
33700         var x = this.xy[0], y = this.xy[1];
33701         var w = this.size.width, h = this.size.height;
33702         if(doShow === true){
33703             if(this.shadow){
33704                 this.shadow.show(this.el);
33705             }
33706             if(this.shim){
33707                 this.shim.show();
33708             }
33709         }
33710         if(this.shadow && this.shadow.isVisible()){
33711             this.shadow.show(this.el);
33712         }
33713         if(this.shim && this.shim.isVisible()){
33714             this.shim.setBounds(x, y, w, h);
33715         }
33716     },
33717
33718     // private
33719     adjustViewport : function(w, h){
33720         if(!w || !h){
33721             w = Roo.lib.Dom.getViewWidth();
33722             h = Roo.lib.Dom.getViewHeight();
33723         }
33724         // cache the size
33725         this.viewSize = [w, h];
33726         if(this.modal && this.mask.isVisible()){
33727             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33728             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33729         }
33730         if(this.isVisible()){
33731             this.constrainXY();
33732         }
33733     },
33734
33735     /**
33736      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33737      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33738      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33739      */
33740     destroy : function(removeEl){
33741         if(this.isVisible()){
33742             this.animateTarget = null;
33743             this.hide();
33744         }
33745         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33746         if(this.tabs){
33747             this.tabs.destroy(removeEl);
33748         }
33749         Roo.destroy(
33750              this.shim,
33751              this.proxy,
33752              this.resizer,
33753              this.close,
33754              this.mask
33755         );
33756         if(this.dd){
33757             this.dd.unreg();
33758         }
33759         if(this.buttons){
33760            for(var i = 0, len = this.buttons.length; i < len; i++){
33761                this.buttons[i].destroy();
33762            }
33763         }
33764         this.el.removeAllListeners();
33765         if(removeEl === true){
33766             this.el.update("");
33767             this.el.remove();
33768         }
33769         Roo.DialogManager.unregister(this);
33770     },
33771
33772     // private
33773     startMove : function(){
33774         if(this.proxyDrag){
33775             this.proxy.show();
33776         }
33777         if(this.constraintoviewport !== false){
33778             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33779         }
33780     },
33781
33782     // private
33783     endMove : function(){
33784         if(!this.proxyDrag){
33785             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33786         }else{
33787             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33788             this.proxy.hide();
33789         }
33790         this.refreshSize();
33791         this.adjustAssets();
33792         this.focus();
33793         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33794     },
33795
33796     /**
33797      * Brings this dialog to the front of any other visible dialogs
33798      * @return {Roo.BasicDialog} this
33799      */
33800     toFront : function(){
33801         Roo.DialogManager.bringToFront(this);
33802         return this;
33803     },
33804
33805     /**
33806      * Sends this dialog to the back (under) of any other visible dialogs
33807      * @return {Roo.BasicDialog} this
33808      */
33809     toBack : function(){
33810         Roo.DialogManager.sendToBack(this);
33811         return this;
33812     },
33813
33814     /**
33815      * Centers this dialog in the viewport
33816      * @return {Roo.BasicDialog} this
33817      */
33818     center : function(){
33819         var xy = this.el.getCenterXY(true);
33820         this.moveTo(xy[0], xy[1]);
33821         return this;
33822     },
33823
33824     /**
33825      * Moves the dialog's top-left corner to the specified point
33826      * @param {Number} x
33827      * @param {Number} y
33828      * @return {Roo.BasicDialog} this
33829      */
33830     moveTo : function(x, y){
33831         this.xy = [x,y];
33832         if(this.isVisible()){
33833             this.el.setXY(this.xy);
33834             this.adjustAssets();
33835         }
33836         return this;
33837     },
33838
33839     /**
33840      * Aligns the dialog to the specified element
33841      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33842      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33843      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33844      * @return {Roo.BasicDialog} this
33845      */
33846     alignTo : function(element, position, offsets){
33847         this.xy = this.el.getAlignToXY(element, position, offsets);
33848         if(this.isVisible()){
33849             this.el.setXY(this.xy);
33850             this.adjustAssets();
33851         }
33852         return this;
33853     },
33854
33855     /**
33856      * Anchors an element to another element and realigns it when the window is resized.
33857      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33858      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33859      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33860      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33861      * is a number, it is used as the buffer delay (defaults to 50ms).
33862      * @return {Roo.BasicDialog} this
33863      */
33864     anchorTo : function(el, alignment, offsets, monitorScroll){
33865         var action = function(){
33866             this.alignTo(el, alignment, offsets);
33867         };
33868         Roo.EventManager.onWindowResize(action, this);
33869         var tm = typeof monitorScroll;
33870         if(tm != 'undefined'){
33871             Roo.EventManager.on(window, 'scroll', action, this,
33872                 {buffer: tm == 'number' ? monitorScroll : 50});
33873         }
33874         action.call(this);
33875         return this;
33876     },
33877
33878     /**
33879      * Returns true if the dialog is visible
33880      * @return {Boolean}
33881      */
33882     isVisible : function(){
33883         return this.el.isVisible();
33884     },
33885
33886     // private
33887     animHide : function(callback){
33888         var b = Roo.get(this.animateTarget).getBox();
33889         this.proxy.show();
33890         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33891         this.el.hide();
33892         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33893                     this.hideEl.createDelegate(this, [callback]));
33894     },
33895
33896     /**
33897      * Hides the dialog.
33898      * @param {Function} callback (optional) Function to call when the dialog is hidden
33899      * @return {Roo.BasicDialog} this
33900      */
33901     hide : function(callback){
33902         if (this.fireEvent("beforehide", this) === false){
33903             return;
33904         }
33905         if(this.shadow){
33906             this.shadow.hide();
33907         }
33908         if(this.shim) {
33909           this.shim.hide();
33910         }
33911         // sometimes animateTarget seems to get set.. causing problems...
33912         // this just double checks..
33913         if(this.animateTarget && Roo.get(this.animateTarget)) {
33914            this.animHide(callback);
33915         }else{
33916             this.el.hide();
33917             this.hideEl(callback);
33918         }
33919         return this;
33920     },
33921
33922     // private
33923     hideEl : function(callback){
33924         this.proxy.hide();
33925         if(this.modal){
33926             this.mask.hide();
33927             Roo.get(document.body).removeClass("x-body-masked");
33928         }
33929         this.fireEvent("hide", this);
33930         if(typeof callback == "function"){
33931             callback();
33932         }
33933     },
33934
33935     // private
33936     hideAction : function(){
33937         this.setLeft("-10000px");
33938         this.setTop("-10000px");
33939         this.setStyle("visibility", "hidden");
33940     },
33941
33942     // private
33943     refreshSize : function(){
33944         this.size = this.el.getSize();
33945         this.xy = this.el.getXY();
33946         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33947     },
33948
33949     // private
33950     // z-index is managed by the DialogManager and may be overwritten at any time
33951     setZIndex : function(index){
33952         if(this.modal){
33953             this.mask.setStyle("z-index", index);
33954         }
33955         if(this.shim){
33956             this.shim.setStyle("z-index", ++index);
33957         }
33958         if(this.shadow){
33959             this.shadow.setZIndex(++index);
33960         }
33961         this.el.setStyle("z-index", ++index);
33962         if(this.proxy){
33963             this.proxy.setStyle("z-index", ++index);
33964         }
33965         if(this.resizer){
33966             this.resizer.proxy.setStyle("z-index", ++index);
33967         }
33968
33969         this.lastZIndex = index;
33970     },
33971
33972     /**
33973      * Returns the element for this dialog
33974      * @return {Roo.Element} The underlying dialog Element
33975      */
33976     getEl : function(){
33977         return this.el;
33978     }
33979 });
33980
33981 /**
33982  * @class Roo.DialogManager
33983  * Provides global access to BasicDialogs that have been created and
33984  * support for z-indexing (layering) multiple open dialogs.
33985  */
33986 Roo.DialogManager = function(){
33987     var list = {};
33988     var accessList = [];
33989     var front = null;
33990
33991     // private
33992     var sortDialogs = function(d1, d2){
33993         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33994     };
33995
33996     // private
33997     var orderDialogs = function(){
33998         accessList.sort(sortDialogs);
33999         var seed = Roo.DialogManager.zseed;
34000         for(var i = 0, len = accessList.length; i < len; i++){
34001             var dlg = accessList[i];
34002             if(dlg){
34003                 dlg.setZIndex(seed + (i*10));
34004             }
34005         }
34006     };
34007
34008     return {
34009         /**
34010          * The starting z-index for BasicDialogs (defaults to 9000)
34011          * @type Number The z-index value
34012          */
34013         zseed : 9000,
34014
34015         // private
34016         register : function(dlg){
34017             list[dlg.id] = dlg;
34018             accessList.push(dlg);
34019         },
34020
34021         // private
34022         unregister : function(dlg){
34023             delete list[dlg.id];
34024             var i=0;
34025             var len=0;
34026             if(!accessList.indexOf){
34027                 for(  i = 0, len = accessList.length; i < len; i++){
34028                     if(accessList[i] == dlg){
34029                         accessList.splice(i, 1);
34030                         return;
34031                     }
34032                 }
34033             }else{
34034                  i = accessList.indexOf(dlg);
34035                 if(i != -1){
34036                     accessList.splice(i, 1);
34037                 }
34038             }
34039         },
34040
34041         /**
34042          * Gets a registered dialog by id
34043          * @param {String/Object} id The id of the dialog or a dialog
34044          * @return {Roo.BasicDialog} this
34045          */
34046         get : function(id){
34047             return typeof id == "object" ? id : list[id];
34048         },
34049
34050         /**
34051          * Brings the specified dialog to the front
34052          * @param {String/Object} dlg The id of the dialog or a dialog
34053          * @return {Roo.BasicDialog} this
34054          */
34055         bringToFront : function(dlg){
34056             dlg = this.get(dlg);
34057             if(dlg != front){
34058                 front = dlg;
34059                 dlg._lastAccess = new Date().getTime();
34060                 orderDialogs();
34061             }
34062             return dlg;
34063         },
34064
34065         /**
34066          * Sends the specified dialog to the back
34067          * @param {String/Object} dlg The id of the dialog or a dialog
34068          * @return {Roo.BasicDialog} this
34069          */
34070         sendToBack : function(dlg){
34071             dlg = this.get(dlg);
34072             dlg._lastAccess = -(new Date().getTime());
34073             orderDialogs();
34074             return dlg;
34075         },
34076
34077         /**
34078          * Hides all dialogs
34079          */
34080         hideAll : function(){
34081             for(var id in list){
34082                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34083                     list[id].hide();
34084                 }
34085             }
34086         }
34087     };
34088 }();
34089
34090 /**
34091  * @class Roo.LayoutDialog
34092  * @extends Roo.BasicDialog
34093  * @children Roo.ContentPanel
34094  * @parent builder none
34095  * Dialog which provides adjustments for working with a layout in a Dialog.
34096  * Add your necessary layout config options to the dialog's config.<br>
34097  * Example usage (including a nested layout):
34098  * <pre><code>
34099 if(!dialog){
34100     dialog = new Roo.LayoutDialog("download-dlg", {
34101         modal: true,
34102         width:600,
34103         height:450,
34104         shadow:true,
34105         minWidth:500,
34106         minHeight:350,
34107         autoTabs:true,
34108         proxyDrag:true,
34109         // layout config merges with the dialog config
34110         center:{
34111             tabPosition: "top",
34112             alwaysShowTabs: true
34113         }
34114     });
34115     dialog.addKeyListener(27, dialog.hide, dialog);
34116     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34117     dialog.addButton("Build It!", this.getDownload, this);
34118
34119     // we can even add nested layouts
34120     var innerLayout = new Roo.BorderLayout("dl-inner", {
34121         east: {
34122             initialSize: 200,
34123             autoScroll:true,
34124             split:true
34125         },
34126         center: {
34127             autoScroll:true
34128         }
34129     });
34130     innerLayout.beginUpdate();
34131     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34132     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34133     innerLayout.endUpdate(true);
34134
34135     var layout = dialog.getLayout();
34136     layout.beginUpdate();
34137     layout.add("center", new Roo.ContentPanel("standard-panel",
34138                         {title: "Download the Source", fitToFrame:true}));
34139     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34140                {title: "Build your own roo.js"}));
34141     layout.getRegion("center").showPanel(sp);
34142     layout.endUpdate();
34143 }
34144 </code></pre>
34145     * @constructor
34146     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34147     * @param {Object} config configuration options
34148   */
34149 Roo.LayoutDialog = function(el, cfg){
34150     
34151     var config=  cfg;
34152     if (typeof(cfg) == 'undefined') {
34153         config = Roo.apply({}, el);
34154         // not sure why we use documentElement here.. - it should always be body.
34155         // IE7 borks horribly if we use documentElement.
34156         // webkit also does not like documentElement - it creates a body element...
34157         el = Roo.get( document.body || document.documentElement ).createChild();
34158         //config.autoCreate = true;
34159     }
34160     
34161     
34162     config.autoTabs = false;
34163     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34164     this.body.setStyle({overflow:"hidden", position:"relative"});
34165     this.layout = new Roo.BorderLayout(this.body.dom, config);
34166     this.layout.monitorWindowResize = false;
34167     this.el.addClass("x-dlg-auto-layout");
34168     // fix case when center region overwrites center function
34169     this.center = Roo.BasicDialog.prototype.center;
34170     this.on("show", this.layout.layout, this.layout, true);
34171     if (config.items) {
34172         var xitems = config.items;
34173         delete config.items;
34174         Roo.each(xitems, this.addxtype, this);
34175     }
34176     
34177     
34178 };
34179 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34180     
34181     
34182     /**
34183      * @cfg {Roo.LayoutRegion} east  
34184      */
34185     /**
34186      * @cfg {Roo.LayoutRegion} west
34187      */
34188     /**
34189      * @cfg {Roo.LayoutRegion} south
34190      */
34191     /**
34192      * @cfg {Roo.LayoutRegion} north
34193      */
34194     /**
34195      * @cfg {Roo.LayoutRegion} center
34196      */
34197     /**
34198      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34199      */
34200     
34201     
34202     /**
34203      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34204      * @deprecated
34205      */
34206     endUpdate : function(){
34207         this.layout.endUpdate();
34208     },
34209
34210     /**
34211      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34212      *  @deprecated
34213      */
34214     beginUpdate : function(){
34215         this.layout.beginUpdate();
34216     },
34217
34218     /**
34219      * Get the BorderLayout for this dialog
34220      * @return {Roo.BorderLayout}
34221      */
34222     getLayout : function(){
34223         return this.layout;
34224     },
34225
34226     showEl : function(){
34227         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34228         if(Roo.isIE7){
34229             this.layout.layout();
34230         }
34231     },
34232
34233     // private
34234     // Use the syncHeightBeforeShow config option to control this automatically
34235     syncBodyHeight : function(){
34236         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34237         if(this.layout){this.layout.layout();}
34238     },
34239     
34240       /**
34241      * Add an xtype element (actually adds to the layout.)
34242      * @return {Object} xdata xtype object data.
34243      */
34244     
34245     addxtype : function(c) {
34246         return this.layout.addxtype(c);
34247     }
34248 });/*
34249  * Based on:
34250  * Ext JS Library 1.1.1
34251  * Copyright(c) 2006-2007, Ext JS, LLC.
34252  *
34253  * Originally Released Under LGPL - original licence link has changed is not relivant.
34254  *
34255  * Fork - LGPL
34256  * <script type="text/javascript">
34257  */
34258  
34259 /**
34260  * @class Roo.MessageBox
34261  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34262  * Example usage:
34263  *<pre><code>
34264 // Basic alert:
34265 Roo.Msg.alert('Status', 'Changes saved successfully.');
34266
34267 // Prompt for user data:
34268 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34269     if (btn == 'ok'){
34270         // process text value...
34271     }
34272 });
34273
34274 // Show a dialog using config options:
34275 Roo.Msg.show({
34276    title:'Save Changes?',
34277    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34278    buttons: Roo.Msg.YESNOCANCEL,
34279    fn: processResult,
34280    animEl: 'elId'
34281 });
34282 </code></pre>
34283  * @static
34284  */
34285 Roo.MessageBox = function(){
34286     var dlg, opt, mask, waitTimer;
34287     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34288     var buttons, activeTextEl, bwidth;
34289
34290     // private
34291     var handleButton = function(button){
34292         dlg.hide();
34293         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34294     };
34295
34296     // private
34297     var handleHide = function(){
34298         if(opt && opt.cls){
34299             dlg.el.removeClass(opt.cls);
34300         }
34301         if(waitTimer){
34302             Roo.TaskMgr.stop(waitTimer);
34303             waitTimer = null;
34304         }
34305     };
34306
34307     // private
34308     var updateButtons = function(b){
34309         var width = 0;
34310         if(!b){
34311             buttons["ok"].hide();
34312             buttons["cancel"].hide();
34313             buttons["yes"].hide();
34314             buttons["no"].hide();
34315             dlg.footer.dom.style.display = 'none';
34316             return width;
34317         }
34318         dlg.footer.dom.style.display = '';
34319         for(var k in buttons){
34320             if(typeof buttons[k] != "function"){
34321                 if(b[k]){
34322                     buttons[k].show();
34323                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34324                     width += buttons[k].el.getWidth()+15;
34325                 }else{
34326                     buttons[k].hide();
34327                 }
34328             }
34329         }
34330         return width;
34331     };
34332
34333     // private
34334     var handleEsc = function(d, k, e){
34335         if(opt && opt.closable !== false){
34336             dlg.hide();
34337         }
34338         if(e){
34339             e.stopEvent();
34340         }
34341     };
34342
34343     return {
34344         /**
34345          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34346          * @return {Roo.BasicDialog} The BasicDialog element
34347          */
34348         getDialog : function(){
34349            if(!dlg){
34350                 dlg = new Roo.BasicDialog("x-msg-box", {
34351                     autoCreate : true,
34352                     shadow: true,
34353                     draggable: true,
34354                     resizable:false,
34355                     constraintoviewport:false,
34356                     fixedcenter:true,
34357                     collapsible : false,
34358                     shim:true,
34359                     modal: true,
34360                     width:400, height:100,
34361                     buttonAlign:"center",
34362                     closeClick : function(){
34363                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34364                             handleButton("no");
34365                         }else{
34366                             handleButton("cancel");
34367                         }
34368                     }
34369                 });
34370                 dlg.on("hide", handleHide);
34371                 mask = dlg.mask;
34372                 dlg.addKeyListener(27, handleEsc);
34373                 buttons = {};
34374                 var bt = this.buttonText;
34375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34379                 bodyEl = dlg.body.createChild({
34380
34381                     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>'
34382                 });
34383                 msgEl = bodyEl.dom.firstChild;
34384                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34385                 textboxEl.enableDisplayMode();
34386                 textboxEl.addKeyListener([10,13], function(){
34387                     if(dlg.isVisible() && opt && opt.buttons){
34388                         if(opt.buttons.ok){
34389                             handleButton("ok");
34390                         }else if(opt.buttons.yes){
34391                             handleButton("yes");
34392                         }
34393                     }
34394                 });
34395                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34396                 textareaEl.enableDisplayMode();
34397                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34398                 progressEl.enableDisplayMode();
34399                 var pf = progressEl.dom.firstChild;
34400                 if (pf) {
34401                     pp = Roo.get(pf.firstChild);
34402                     pp.setHeight(pf.offsetHeight);
34403                 }
34404                 
34405             }
34406             return dlg;
34407         },
34408
34409         /**
34410          * Updates the message box body text
34411          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34412          * the XHTML-compliant non-breaking space character '&amp;#160;')
34413          * @return {Roo.MessageBox} This message box
34414          */
34415         updateText : function(text){
34416             if(!dlg.isVisible() && !opt.width){
34417                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34418             }
34419             msgEl.innerHTML = text || '&#160;';
34420       
34421             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34422             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34423             var w = Math.max(
34424                     Math.min(opt.width || cw , this.maxWidth), 
34425                     Math.max(opt.minWidth || this.minWidth, bwidth)
34426             );
34427             if(opt.prompt){
34428                 activeTextEl.setWidth(w);
34429             }
34430             if(dlg.isVisible()){
34431                 dlg.fixedcenter = false;
34432             }
34433             // to big, make it scroll. = But as usual stupid IE does not support
34434             // !important..
34435             
34436             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34437                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34438                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34439             } else {
34440                 bodyEl.dom.style.height = '';
34441                 bodyEl.dom.style.overflowY = '';
34442             }
34443             if (cw > w) {
34444                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34445             } else {
34446                 bodyEl.dom.style.overflowX = '';
34447             }
34448             
34449             dlg.setContentSize(w, bodyEl.getHeight());
34450             if(dlg.isVisible()){
34451                 dlg.fixedcenter = true;
34452             }
34453             return this;
34454         },
34455
34456         /**
34457          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34458          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34459          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34460          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34461          * @return {Roo.MessageBox} This message box
34462          */
34463         updateProgress : function(value, text){
34464             if(text){
34465                 this.updateText(text);
34466             }
34467             if (pp) { // weird bug on my firefox - for some reason this is not defined
34468                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34469             }
34470             return this;
34471         },        
34472
34473         /**
34474          * Returns true if the message box is currently displayed
34475          * @return {Boolean} True if the message box is visible, else false
34476          */
34477         isVisible : function(){
34478             return dlg && dlg.isVisible();  
34479         },
34480
34481         /**
34482          * Hides the message box if it is displayed
34483          */
34484         hide : function(){
34485             if(this.isVisible()){
34486                 dlg.hide();
34487             }  
34488         },
34489
34490         /**
34491          * Displays a new message box, or reinitializes an existing message box, based on the config options
34492          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34493          * The following config object properties are supported:
34494          * <pre>
34495 Property    Type             Description
34496 ----------  ---------------  ------------------------------------------------------------------------------------
34497 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34498                                    closes (defaults to undefined)
34499 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34500                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34501 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34502                                    progress and wait dialogs will ignore this property and always hide the
34503                                    close button as they can only be closed programmatically.
34504 cls               String           A custom CSS class to apply to the message box element
34505 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34506                                    displayed (defaults to 75)
34507 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34508                                    function will be btn (the name of the button that was clicked, if applicable,
34509                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34510                                    Progress and wait dialogs will ignore this option since they do not respond to
34511                                    user actions and can only be closed programmatically, so any required function
34512                                    should be called by the same code after it closes the dialog.
34513 icon              String           A CSS class that provides a background image to be used as an icon for
34514                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34515 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34516 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34517 modal             Boolean          False to allow user interaction with the page while the message box is
34518                                    displayed (defaults to true)
34519 msg               String           A string that will replace the existing message box body text (defaults
34520                                    to the XHTML-compliant non-breaking space character '&#160;')
34521 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34522 progress          Boolean          True to display a progress bar (defaults to false)
34523 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34524 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34525 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34526 title             String           The title text
34527 value             String           The string value to set into the active textbox element if displayed
34528 wait              Boolean          True to display a progress bar (defaults to false)
34529 width             Number           The width of the dialog in pixels
34530 </pre>
34531          *
34532          * Example usage:
34533          * <pre><code>
34534 Roo.Msg.show({
34535    title: 'Address',
34536    msg: 'Please enter your address:',
34537    width: 300,
34538    buttons: Roo.MessageBox.OKCANCEL,
34539    multiline: true,
34540    fn: saveAddress,
34541    animEl: 'addAddressBtn'
34542 });
34543 </code></pre>
34544          * @param {Object} config Configuration options
34545          * @return {Roo.MessageBox} This message box
34546          */
34547         show : function(options)
34548         {
34549             
34550             // this causes nightmares if you show one dialog after another
34551             // especially on callbacks..
34552              
34553             if(this.isVisible()){
34554                 
34555                 this.hide();
34556                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34557                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34558                 Roo.log("New Dialog Message:" +  options.msg )
34559                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34560                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34561                 
34562             }
34563             var d = this.getDialog();
34564             opt = options;
34565             d.setTitle(opt.title || "&#160;");
34566             d.close.setDisplayed(opt.closable !== false);
34567             activeTextEl = textboxEl;
34568             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34569             if(opt.prompt){
34570                 if(opt.multiline){
34571                     textboxEl.hide();
34572                     textareaEl.show();
34573                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34574                         opt.multiline : this.defaultTextHeight);
34575                     activeTextEl = textareaEl;
34576                 }else{
34577                     textboxEl.show();
34578                     textareaEl.hide();
34579                 }
34580             }else{
34581                 textboxEl.hide();
34582                 textareaEl.hide();
34583             }
34584             progressEl.setDisplayed(opt.progress === true);
34585             this.updateProgress(0);
34586             activeTextEl.dom.value = opt.value || "";
34587             if(opt.prompt){
34588                 dlg.setDefaultButton(activeTextEl);
34589             }else{
34590                 var bs = opt.buttons;
34591                 var db = null;
34592                 if(bs && bs.ok){
34593                     db = buttons["ok"];
34594                 }else if(bs && bs.yes){
34595                     db = buttons["yes"];
34596                 }
34597                 dlg.setDefaultButton(db);
34598             }
34599             bwidth = updateButtons(opt.buttons);
34600             this.updateText(opt.msg);
34601             if(opt.cls){
34602                 d.el.addClass(opt.cls);
34603             }
34604             d.proxyDrag = opt.proxyDrag === true;
34605             d.modal = opt.modal !== false;
34606             d.mask = opt.modal !== false ? mask : false;
34607             if(!d.isVisible()){
34608                 // force it to the end of the z-index stack so it gets a cursor in FF
34609                 document.body.appendChild(dlg.el.dom);
34610                 d.animateTarget = null;
34611                 d.show(options.animEl);
34612             }
34613             return this;
34614         },
34615
34616         /**
34617          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34618          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34619          * and closing the message box when the process is complete.
34620          * @param {String} title The title bar text
34621          * @param {String} msg The message box body text
34622          * @return {Roo.MessageBox} This message box
34623          */
34624         progress : function(title, msg){
34625             this.show({
34626                 title : title,
34627                 msg : msg,
34628                 buttons: false,
34629                 progress:true,
34630                 closable:false,
34631                 minWidth: this.minProgressWidth,
34632                 modal : true
34633             });
34634             return this;
34635         },
34636
34637         /**
34638          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34639          * If a callback function is passed it will be called after the user clicks the button, and the
34640          * id of the button that was clicked will be passed as the only parameter to the callback
34641          * (could also be the top-right close button).
34642          * @param {String} title The title bar text
34643          * @param {String} msg The message box body text
34644          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34645          * @param {Object} scope (optional) The scope of the callback function
34646          * @return {Roo.MessageBox} This message box
34647          */
34648         alert : function(title, msg, fn, scope){
34649             this.show({
34650                 title : title,
34651                 msg : msg,
34652                 buttons: this.OK,
34653                 fn: fn,
34654                 scope : scope,
34655                 modal : true
34656             });
34657             return this;
34658         },
34659
34660         /**
34661          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34662          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34663          * You are responsible for closing the message box when the process is complete.
34664          * @param {String} msg The message box body text
34665          * @param {String} title (optional) The title bar text
34666          * @return {Roo.MessageBox} This message box
34667          */
34668         wait : function(msg, title){
34669             this.show({
34670                 title : title,
34671                 msg : msg,
34672                 buttons: false,
34673                 closable:false,
34674                 progress:true,
34675                 modal:true,
34676                 width:300,
34677                 wait:true
34678             });
34679             waitTimer = Roo.TaskMgr.start({
34680                 run: function(i){
34681                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34682                 },
34683                 interval: 1000
34684             });
34685             return this;
34686         },
34687
34688         /**
34689          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34690          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34691          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34692          * @param {String} title The title bar text
34693          * @param {String} msg The message box body text
34694          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34695          * @param {Object} scope (optional) The scope of the callback function
34696          * @return {Roo.MessageBox} This message box
34697          */
34698         confirm : function(title, msg, fn, scope){
34699             this.show({
34700                 title : title,
34701                 msg : msg,
34702                 buttons: this.YESNO,
34703                 fn: fn,
34704                 scope : scope,
34705                 modal : true
34706             });
34707             return this;
34708         },
34709
34710         /**
34711          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34712          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34713          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34714          * (could also be the top-right close button) and the text that was entered will be passed as the two
34715          * parameters to the callback.
34716          * @param {String} title The title bar text
34717          * @param {String} msg The message box body text
34718          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34719          * @param {Object} scope (optional) The scope of the callback function
34720          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34721          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34722          * @return {Roo.MessageBox} This message box
34723          */
34724         prompt : function(title, msg, fn, scope, multiline){
34725             this.show({
34726                 title : title,
34727                 msg : msg,
34728                 buttons: this.OKCANCEL,
34729                 fn: fn,
34730                 minWidth:250,
34731                 scope : scope,
34732                 prompt:true,
34733                 multiline: multiline,
34734                 modal : true
34735             });
34736             return this;
34737         },
34738
34739         /**
34740          * Button config that displays a single OK button
34741          * @type Object
34742          */
34743         OK : {ok:true},
34744         /**
34745          * Button config that displays Yes and No buttons
34746          * @type Object
34747          */
34748         YESNO : {yes:true, no:true},
34749         /**
34750          * Button config that displays OK and Cancel buttons
34751          * @type Object
34752          */
34753         OKCANCEL : {ok:true, cancel:true},
34754         /**
34755          * Button config that displays Yes, No and Cancel buttons
34756          * @type Object
34757          */
34758         YESNOCANCEL : {yes:true, no:true, cancel:true},
34759
34760         /**
34761          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34762          * @type Number
34763          */
34764         defaultTextHeight : 75,
34765         /**
34766          * The maximum width in pixels of the message box (defaults to 600)
34767          * @type Number
34768          */
34769         maxWidth : 600,
34770         /**
34771          * The minimum width in pixels of the message box (defaults to 100)
34772          * @type Number
34773          */
34774         minWidth : 100,
34775         /**
34776          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34777          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34778          * @type Number
34779          */
34780         minProgressWidth : 250,
34781         /**
34782          * An object containing the default button text strings that can be overriden for localized language support.
34783          * Supported properties are: ok, cancel, yes and no.
34784          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34785          * @type Object
34786          */
34787         buttonText : {
34788             ok : "OK",
34789             cancel : "Cancel",
34790             yes : "Yes",
34791             no : "No"
34792         }
34793     };
34794 }();
34795
34796 /**
34797  * Shorthand for {@link Roo.MessageBox}
34798  */
34799 Roo.Msg = Roo.MessageBox;/*
34800  * Based on:
34801  * Ext JS Library 1.1.1
34802  * Copyright(c) 2006-2007, Ext JS, LLC.
34803  *
34804  * Originally Released Under LGPL - original licence link has changed is not relivant.
34805  *
34806  * Fork - LGPL
34807  * <script type="text/javascript">
34808  */
34809 /**
34810  * @class Roo.QuickTips
34811  * Provides attractive and customizable tooltips for any element.
34812  * @static
34813  */
34814 Roo.QuickTips = function(){
34815     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34816     var ce, bd, xy, dd;
34817     var visible = false, disabled = true, inited = false;
34818     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34819     
34820     var onOver = function(e){
34821         if(disabled){
34822             return;
34823         }
34824         var t = e.getTarget();
34825         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34826             return;
34827         }
34828         if(ce && t == ce.el){
34829             clearTimeout(hideProc);
34830             return;
34831         }
34832         if(t && tagEls[t.id]){
34833             tagEls[t.id].el = t;
34834             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34835             return;
34836         }
34837         var ttp, et = Roo.fly(t);
34838         var ns = cfg.namespace;
34839         if(tm.interceptTitles && t.title){
34840             ttp = t.title;
34841             t.qtip = ttp;
34842             t.removeAttribute("title");
34843             e.preventDefault();
34844         }else{
34845             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34846         }
34847         if(ttp){
34848             showProc = show.defer(tm.showDelay, tm, [{
34849                 el: t, 
34850                 text: ttp.replace(/\\n/g,'<br/>'),
34851                 width: et.getAttributeNS(ns, cfg.width),
34852                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34853                 title: et.getAttributeNS(ns, cfg.title),
34854                     cls: et.getAttributeNS(ns, cfg.cls)
34855             }]);
34856         }
34857     };
34858     
34859     var onOut = function(e){
34860         clearTimeout(showProc);
34861         var t = e.getTarget();
34862         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34863             hideProc = setTimeout(hide, tm.hideDelay);
34864         }
34865     };
34866     
34867     var onMove = function(e){
34868         if(disabled){
34869             return;
34870         }
34871         xy = e.getXY();
34872         xy[1] += 18;
34873         if(tm.trackMouse && ce){
34874             el.setXY(xy);
34875         }
34876     };
34877     
34878     var onDown = function(e){
34879         clearTimeout(showProc);
34880         clearTimeout(hideProc);
34881         if(!e.within(el)){
34882             if(tm.hideOnClick){
34883                 hide();
34884                 tm.disable();
34885                 tm.enable.defer(100, tm);
34886             }
34887         }
34888     };
34889     
34890     var getPad = function(){
34891         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34892     };
34893
34894     var show = function(o){
34895         if(disabled){
34896             return;
34897         }
34898         clearTimeout(dismissProc);
34899         ce = o;
34900         if(removeCls){ // in case manually hidden
34901             el.removeClass(removeCls);
34902             removeCls = null;
34903         }
34904         if(ce.cls){
34905             el.addClass(ce.cls);
34906             removeCls = ce.cls;
34907         }
34908         if(ce.title){
34909             tipTitle.update(ce.title);
34910             tipTitle.show();
34911         }else{
34912             tipTitle.update('');
34913             tipTitle.hide();
34914         }
34915         el.dom.style.width  = tm.maxWidth+'px';
34916         //tipBody.dom.style.width = '';
34917         tipBodyText.update(o.text);
34918         var p = getPad(), w = ce.width;
34919         if(!w){
34920             var td = tipBodyText.dom;
34921             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34922             if(aw > tm.maxWidth){
34923                 w = tm.maxWidth;
34924             }else if(aw < tm.minWidth){
34925                 w = tm.minWidth;
34926             }else{
34927                 w = aw;
34928             }
34929         }
34930         //tipBody.setWidth(w);
34931         el.setWidth(parseInt(w, 10) + p);
34932         if(ce.autoHide === false){
34933             close.setDisplayed(true);
34934             if(dd){
34935                 dd.unlock();
34936             }
34937         }else{
34938             close.setDisplayed(false);
34939             if(dd){
34940                 dd.lock();
34941             }
34942         }
34943         if(xy){
34944             el.avoidY = xy[1]-18;
34945             el.setXY(xy);
34946         }
34947         if(tm.animate){
34948             el.setOpacity(.1);
34949             el.setStyle("visibility", "visible");
34950             el.fadeIn({callback: afterShow});
34951         }else{
34952             afterShow();
34953         }
34954     };
34955     
34956     var afterShow = function(){
34957         if(ce){
34958             el.show();
34959             esc.enable();
34960             if(tm.autoDismiss && ce.autoHide !== false){
34961                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34962             }
34963         }
34964     };
34965     
34966     var hide = function(noanim){
34967         clearTimeout(dismissProc);
34968         clearTimeout(hideProc);
34969         ce = null;
34970         if(el.isVisible()){
34971             esc.disable();
34972             if(noanim !== true && tm.animate){
34973                 el.fadeOut({callback: afterHide});
34974             }else{
34975                 afterHide();
34976             } 
34977         }
34978     };
34979     
34980     var afterHide = function(){
34981         el.hide();
34982         if(removeCls){
34983             el.removeClass(removeCls);
34984             removeCls = null;
34985         }
34986     };
34987     
34988     return {
34989         /**
34990         * @cfg {Number} minWidth
34991         * The minimum width of the quick tip (defaults to 40)
34992         */
34993        minWidth : 40,
34994         /**
34995         * @cfg {Number} maxWidth
34996         * The maximum width of the quick tip (defaults to 300)
34997         */
34998        maxWidth : 300,
34999         /**
35000         * @cfg {Boolean} interceptTitles
35001         * True to automatically use the element's DOM title value if available (defaults to false)
35002         */
35003        interceptTitles : false,
35004         /**
35005         * @cfg {Boolean} trackMouse
35006         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35007         */
35008        trackMouse : false,
35009         /**
35010         * @cfg {Boolean} hideOnClick
35011         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35012         */
35013        hideOnClick : true,
35014         /**
35015         * @cfg {Number} showDelay
35016         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35017         */
35018        showDelay : 500,
35019         /**
35020         * @cfg {Number} hideDelay
35021         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35022         */
35023        hideDelay : 200,
35024         /**
35025         * @cfg {Boolean} autoHide
35026         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35027         * Used in conjunction with hideDelay.
35028         */
35029        autoHide : true,
35030         /**
35031         * @cfg {Boolean}
35032         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35033         * (defaults to true).  Used in conjunction with autoDismissDelay.
35034         */
35035        autoDismiss : true,
35036         /**
35037         * @cfg {Number}
35038         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35039         */
35040        autoDismissDelay : 5000,
35041        /**
35042         * @cfg {Boolean} animate
35043         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35044         */
35045        animate : false,
35046
35047        /**
35048         * @cfg {String} title
35049         * Title text to display (defaults to '').  This can be any valid HTML markup.
35050         */
35051         title: '',
35052        /**
35053         * @cfg {String} text
35054         * Body text to display (defaults to '').  This can be any valid HTML markup.
35055         */
35056         text : '',
35057        /**
35058         * @cfg {String} cls
35059         * A CSS class to apply to the base quick tip element (defaults to '').
35060         */
35061         cls : '',
35062        /**
35063         * @cfg {Number} width
35064         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35065         * minWidth or maxWidth.
35066         */
35067         width : null,
35068
35069     /**
35070      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35071      * or display QuickTips in a page.
35072      */
35073        init : function(){
35074           tm = Roo.QuickTips;
35075           cfg = tm.tagConfig;
35076           if(!inited){
35077               if(!Roo.isReady){ // allow calling of init() before onReady
35078                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35079                   return;
35080               }
35081               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35082               el.fxDefaults = {stopFx: true};
35083               // maximum custom styling
35084               //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>');
35085               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>');              
35086               tipTitle = el.child('h3');
35087               tipTitle.enableDisplayMode("block");
35088               tipBody = el.child('div.x-tip-bd');
35089               tipBodyText = el.child('div.x-tip-bd-inner');
35090               //bdLeft = el.child('div.x-tip-bd-left');
35091               //bdRight = el.child('div.x-tip-bd-right');
35092               close = el.child('div.x-tip-close');
35093               close.enableDisplayMode("block");
35094               close.on("click", hide);
35095               var d = Roo.get(document);
35096               d.on("mousedown", onDown);
35097               d.on("mouseover", onOver);
35098               d.on("mouseout", onOut);
35099               d.on("mousemove", onMove);
35100               esc = d.addKeyListener(27, hide);
35101               esc.disable();
35102               if(Roo.dd.DD){
35103                   dd = el.initDD("default", null, {
35104                       onDrag : function(){
35105                           el.sync();  
35106                       }
35107                   });
35108                   dd.setHandleElId(tipTitle.id);
35109                   dd.lock();
35110               }
35111               inited = true;
35112           }
35113           this.enable(); 
35114        },
35115
35116     /**
35117      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35118      * are supported:
35119      * <pre>
35120 Property    Type                   Description
35121 ----------  ---------------------  ------------------------------------------------------------------------
35122 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35123      * </ul>
35124      * @param {Object} config The config object
35125      */
35126        register : function(config){
35127            var cs = config instanceof Array ? config : arguments;
35128            for(var i = 0, len = cs.length; i < len; i++) {
35129                var c = cs[i];
35130                var target = c.target;
35131                if(target){
35132                    if(target instanceof Array){
35133                        for(var j = 0, jlen = target.length; j < jlen; j++){
35134                            tagEls[target[j]] = c;
35135                        }
35136                    }else{
35137                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35138                    }
35139                }
35140            }
35141        },
35142
35143     /**
35144      * Removes this quick tip from its element and destroys it.
35145      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35146      */
35147        unregister : function(el){
35148            delete tagEls[Roo.id(el)];
35149        },
35150
35151     /**
35152      * Enable this quick tip.
35153      */
35154        enable : function(){
35155            if(inited && disabled){
35156                locks.pop();
35157                if(locks.length < 1){
35158                    disabled = false;
35159                }
35160            }
35161        },
35162
35163     /**
35164      * Disable this quick tip.
35165      */
35166        disable : function(){
35167           disabled = true;
35168           clearTimeout(showProc);
35169           clearTimeout(hideProc);
35170           clearTimeout(dismissProc);
35171           if(ce){
35172               hide(true);
35173           }
35174           locks.push(1);
35175        },
35176
35177     /**
35178      * Returns true if the quick tip is enabled, else false.
35179      */
35180        isEnabled : function(){
35181             return !disabled;
35182        },
35183
35184         // private
35185        tagConfig : {
35186            namespace : "roo", // was ext?? this may break..
35187            alt_namespace : "ext",
35188            attribute : "qtip",
35189            width : "width",
35190            target : "target",
35191            title : "qtitle",
35192            hide : "hide",
35193            cls : "qclass"
35194        }
35195    };
35196 }();
35197
35198 // backwards compat
35199 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35200  * Based on:
35201  * Ext JS Library 1.1.1
35202  * Copyright(c) 2006-2007, Ext JS, LLC.
35203  *
35204  * Originally Released Under LGPL - original licence link has changed is not relivant.
35205  *
35206  * Fork - LGPL
35207  * <script type="text/javascript">
35208  */
35209  
35210
35211 /**
35212  * @class Roo.tree.TreePanel
35213  * @extends Roo.data.Tree
35214  * @cfg {Roo.tree.TreeNode} root The root node
35215  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35216  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35217  * @cfg {Boolean} enableDD true to enable drag and drop
35218  * @cfg {Boolean} enableDrag true to enable just drag
35219  * @cfg {Boolean} enableDrop true to enable just drop
35220  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35221  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35222  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35223  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35224  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35225  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35226  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35227  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35228  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35229  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35230  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35231  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35232  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35233  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35234  * @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>
35235  * @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>
35236  * 
35237  * @constructor
35238  * @param {String/HTMLElement/Element} el The container element
35239  * @param {Object} config
35240  */
35241 Roo.tree.TreePanel = function(el, config){
35242     var root = false;
35243     var loader = false;
35244     if (config.root) {
35245         root = config.root;
35246         delete config.root;
35247     }
35248     if (config.loader) {
35249         loader = config.loader;
35250         delete config.loader;
35251     }
35252     
35253     Roo.apply(this, config);
35254     Roo.tree.TreePanel.superclass.constructor.call(this);
35255     this.el = Roo.get(el);
35256     this.el.addClass('x-tree');
35257     //console.log(root);
35258     if (root) {
35259         this.setRootNode( Roo.factory(root, Roo.tree));
35260     }
35261     if (loader) {
35262         this.loader = Roo.factory(loader, Roo.tree);
35263     }
35264    /**
35265     * Read-only. The id of the container element becomes this TreePanel's id.
35266     */
35267     this.id = this.el.id;
35268     this.addEvents({
35269         /**
35270         * @event beforeload
35271         * Fires before a node is loaded, return false to cancel
35272         * @param {Node} node The node being loaded
35273         */
35274         "beforeload" : true,
35275         /**
35276         * @event load
35277         * Fires when a node is loaded
35278         * @param {Node} node The node that was loaded
35279         */
35280         "load" : true,
35281         /**
35282         * @event textchange
35283         * Fires when the text for a node is changed
35284         * @param {Node} node The node
35285         * @param {String} text The new text
35286         * @param {String} oldText The old text
35287         */
35288         "textchange" : true,
35289         /**
35290         * @event beforeexpand
35291         * Fires before a node is expanded, return false to cancel.
35292         * @param {Node} node The node
35293         * @param {Boolean} deep
35294         * @param {Boolean} anim
35295         */
35296         "beforeexpand" : true,
35297         /**
35298         * @event beforecollapse
35299         * Fires before a node is collapsed, return false to cancel.
35300         * @param {Node} node The node
35301         * @param {Boolean} deep
35302         * @param {Boolean} anim
35303         */
35304         "beforecollapse" : true,
35305         /**
35306         * @event expand
35307         * Fires when a node is expanded
35308         * @param {Node} node The node
35309         */
35310         "expand" : true,
35311         /**
35312         * @event disabledchange
35313         * Fires when the disabled status of a node changes
35314         * @param {Node} node The node
35315         * @param {Boolean} disabled
35316         */
35317         "disabledchange" : true,
35318         /**
35319         * @event collapse
35320         * Fires when a node is collapsed
35321         * @param {Node} node The node
35322         */
35323         "collapse" : true,
35324         /**
35325         * @event beforeclick
35326         * Fires before click processing on a node. Return false to cancel the default action.
35327         * @param {Node} node The node
35328         * @param {Roo.EventObject} e The event object
35329         */
35330         "beforeclick":true,
35331         /**
35332         * @event checkchange
35333         * Fires when a node with a checkbox's checked property changes
35334         * @param {Node} this This node
35335         * @param {Boolean} checked
35336         */
35337         "checkchange":true,
35338         /**
35339         * @event click
35340         * Fires when a node is clicked
35341         * @param {Node} node The node
35342         * @param {Roo.EventObject} e The event object
35343         */
35344         "click":true,
35345         /**
35346         * @event dblclick
35347         * Fires when a node is double clicked
35348         * @param {Node} node The node
35349         * @param {Roo.EventObject} e The event object
35350         */
35351         "dblclick":true,
35352         /**
35353         * @event contextmenu
35354         * Fires when a node is right clicked
35355         * @param {Node} node The node
35356         * @param {Roo.EventObject} e The event object
35357         */
35358         "contextmenu":true,
35359         /**
35360         * @event beforechildrenrendered
35361         * Fires right before the child nodes for a node are rendered
35362         * @param {Node} node The node
35363         */
35364         "beforechildrenrendered":true,
35365         /**
35366         * @event startdrag
35367         * Fires when a node starts being dragged
35368         * @param {Roo.tree.TreePanel} this
35369         * @param {Roo.tree.TreeNode} node
35370         * @param {event} e The raw browser event
35371         */ 
35372        "startdrag" : true,
35373        /**
35374         * @event enddrag
35375         * Fires when a drag operation is complete
35376         * @param {Roo.tree.TreePanel} this
35377         * @param {Roo.tree.TreeNode} node
35378         * @param {event} e The raw browser event
35379         */
35380        "enddrag" : true,
35381        /**
35382         * @event dragdrop
35383         * Fires when a dragged node is dropped on a valid DD target
35384         * @param {Roo.tree.TreePanel} this
35385         * @param {Roo.tree.TreeNode} node
35386         * @param {DD} dd The dd it was dropped on
35387         * @param {event} e The raw browser event
35388         */
35389        "dragdrop" : true,
35390        /**
35391         * @event beforenodedrop
35392         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35393         * passed to handlers has the following properties:<br />
35394         * <ul style="padding:5px;padding-left:16px;">
35395         * <li>tree - The TreePanel</li>
35396         * <li>target - The node being targeted for the drop</li>
35397         * <li>data - The drag data from the drag source</li>
35398         * <li>point - The point of the drop - append, above or below</li>
35399         * <li>source - The drag source</li>
35400         * <li>rawEvent - Raw mouse event</li>
35401         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35402         * to be inserted by setting them on this object.</li>
35403         * <li>cancel - Set this to true to cancel the drop.</li>
35404         * </ul>
35405         * @param {Object} dropEvent
35406         */
35407        "beforenodedrop" : true,
35408        /**
35409         * @event nodedrop
35410         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35411         * passed to handlers has the following properties:<br />
35412         * <ul style="padding:5px;padding-left:16px;">
35413         * <li>tree - The TreePanel</li>
35414         * <li>target - The node being targeted for the drop</li>
35415         * <li>data - The drag data from the drag source</li>
35416         * <li>point - The point of the drop - append, above or below</li>
35417         * <li>source - The drag source</li>
35418         * <li>rawEvent - Raw mouse event</li>
35419         * <li>dropNode - Dropped node(s).</li>
35420         * </ul>
35421         * @param {Object} dropEvent
35422         */
35423        "nodedrop" : true,
35424         /**
35425         * @event nodedragover
35426         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35427         * passed to handlers has the following properties:<br />
35428         * <ul style="padding:5px;padding-left:16px;">
35429         * <li>tree - The TreePanel</li>
35430         * <li>target - The node being targeted for the drop</li>
35431         * <li>data - The drag data from the drag source</li>
35432         * <li>point - The point of the drop - append, above or below</li>
35433         * <li>source - The drag source</li>
35434         * <li>rawEvent - Raw mouse event</li>
35435         * <li>dropNode - Drop node(s) provided by the source.</li>
35436         * <li>cancel - Set this to true to signal drop not allowed.</li>
35437         * </ul>
35438         * @param {Object} dragOverEvent
35439         */
35440        "nodedragover" : true,
35441        /**
35442         * @event appendnode
35443         * Fires when append node to the tree
35444         * @param {Roo.tree.TreePanel} this
35445         * @param {Roo.tree.TreeNode} node
35446         * @param {Number} index The index of the newly appended node
35447         */
35448        "appendnode" : true
35449         
35450     });
35451     if(this.singleExpand){
35452        this.on("beforeexpand", this.restrictExpand, this);
35453     }
35454     if (this.editor) {
35455         this.editor.tree = this;
35456         this.editor = Roo.factory(this.editor, Roo.tree);
35457     }
35458     
35459     if (this.selModel) {
35460         this.selModel = Roo.factory(this.selModel, Roo.tree);
35461     }
35462    
35463 };
35464 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35465     rootVisible : true,
35466     animate: Roo.enableFx,
35467     lines : true,
35468     enableDD : false,
35469     hlDrop : Roo.enableFx,
35470   
35471     renderer: false,
35472     
35473     rendererTip: false,
35474     // private
35475     restrictExpand : function(node){
35476         var p = node.parentNode;
35477         if(p){
35478             if(p.expandedChild && p.expandedChild.parentNode == p){
35479                 p.expandedChild.collapse();
35480             }
35481             p.expandedChild = node;
35482         }
35483     },
35484
35485     // private override
35486     setRootNode : function(node){
35487         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35488         if(!this.rootVisible){
35489             node.ui = new Roo.tree.RootTreeNodeUI(node);
35490         }
35491         return node;
35492     },
35493
35494     /**
35495      * Returns the container element for this TreePanel
35496      */
35497     getEl : function(){
35498         return this.el;
35499     },
35500
35501     /**
35502      * Returns the default TreeLoader for this TreePanel
35503      */
35504     getLoader : function(){
35505         return this.loader;
35506     },
35507
35508     /**
35509      * Expand all nodes
35510      */
35511     expandAll : function(){
35512         this.root.expand(true);
35513     },
35514
35515     /**
35516      * Collapse all nodes
35517      */
35518     collapseAll : function(){
35519         this.root.collapse(true);
35520     },
35521
35522     /**
35523      * Returns the selection model used by this TreePanel
35524      */
35525     getSelectionModel : function(){
35526         if(!this.selModel){
35527             this.selModel = new Roo.tree.DefaultSelectionModel();
35528         }
35529         return this.selModel;
35530     },
35531
35532     /**
35533      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35534      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35535      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35536      * @return {Array}
35537      */
35538     getChecked : function(a, startNode){
35539         startNode = startNode || this.root;
35540         var r = [];
35541         var f = function(){
35542             if(this.attributes.checked){
35543                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35544             }
35545         }
35546         startNode.cascade(f);
35547         return r;
35548     },
35549
35550     /**
35551      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35552      * @param {String} path
35553      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35554      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35555      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35556      */
35557     expandPath : function(path, attr, callback){
35558         attr = attr || "id";
35559         var keys = path.split(this.pathSeparator);
35560         var curNode = this.root;
35561         if(curNode.attributes[attr] != keys[1]){ // invalid root
35562             if(callback){
35563                 callback(false, null);
35564             }
35565             return;
35566         }
35567         var index = 1;
35568         var f = function(){
35569             if(++index == keys.length){
35570                 if(callback){
35571                     callback(true, curNode);
35572                 }
35573                 return;
35574             }
35575             var c = curNode.findChild(attr, keys[index]);
35576             if(!c){
35577                 if(callback){
35578                     callback(false, curNode);
35579                 }
35580                 return;
35581             }
35582             curNode = c;
35583             c.expand(false, false, f);
35584         };
35585         curNode.expand(false, false, f);
35586     },
35587
35588     /**
35589      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35590      * @param {String} path
35591      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35592      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35593      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35594      */
35595     selectPath : function(path, attr, callback){
35596         attr = attr || "id";
35597         var keys = path.split(this.pathSeparator);
35598         var v = keys.pop();
35599         if(keys.length > 0){
35600             var f = function(success, node){
35601                 if(success && node){
35602                     var n = node.findChild(attr, v);
35603                     if(n){
35604                         n.select();
35605                         if(callback){
35606                             callback(true, n);
35607                         }
35608                     }else if(callback){
35609                         callback(false, n);
35610                     }
35611                 }else{
35612                     if(callback){
35613                         callback(false, n);
35614                     }
35615                 }
35616             };
35617             this.expandPath(keys.join(this.pathSeparator), attr, f);
35618         }else{
35619             this.root.select();
35620             if(callback){
35621                 callback(true, this.root);
35622             }
35623         }
35624     },
35625
35626     getTreeEl : function(){
35627         return this.el;
35628     },
35629
35630     /**
35631      * Trigger rendering of this TreePanel
35632      */
35633     render : function(){
35634         if (this.innerCt) {
35635             return this; // stop it rendering more than once!!
35636         }
35637         
35638         this.innerCt = this.el.createChild({tag:"ul",
35639                cls:"x-tree-root-ct " +
35640                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35641
35642         if(this.containerScroll){
35643             Roo.dd.ScrollManager.register(this.el);
35644         }
35645         if((this.enableDD || this.enableDrop) && !this.dropZone){
35646            /**
35647             * The dropZone used by this tree if drop is enabled
35648             * @type Roo.tree.TreeDropZone
35649             */
35650              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35651                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35652            });
35653         }
35654         if((this.enableDD || this.enableDrag) && !this.dragZone){
35655            /**
35656             * The dragZone used by this tree if drag is enabled
35657             * @type Roo.tree.TreeDragZone
35658             */
35659             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35660                ddGroup: this.ddGroup || "TreeDD",
35661                scroll: this.ddScroll
35662            });
35663         }
35664         this.getSelectionModel().init(this);
35665         if (!this.root) {
35666             Roo.log("ROOT not set in tree");
35667             return this;
35668         }
35669         this.root.render();
35670         if(!this.rootVisible){
35671             this.root.renderChildren();
35672         }
35673         return this;
35674     }
35675 });/*
35676  * Based on:
35677  * Ext JS Library 1.1.1
35678  * Copyright(c) 2006-2007, Ext JS, LLC.
35679  *
35680  * Originally Released Under LGPL - original licence link has changed is not relivant.
35681  *
35682  * Fork - LGPL
35683  * <script type="text/javascript">
35684  */
35685  
35686
35687 /**
35688  * @class Roo.tree.DefaultSelectionModel
35689  * @extends Roo.util.Observable
35690  * The default single selection for a TreePanel.
35691  * @param {Object} cfg Configuration
35692  */
35693 Roo.tree.DefaultSelectionModel = function(cfg){
35694    this.selNode = null;
35695    
35696    
35697    
35698    this.addEvents({
35699        /**
35700         * @event selectionchange
35701         * Fires when the selected node changes
35702         * @param {DefaultSelectionModel} this
35703         * @param {TreeNode} node the new selection
35704         */
35705        "selectionchange" : true,
35706
35707        /**
35708         * @event beforeselect
35709         * Fires before the selected node changes, return false to cancel the change
35710         * @param {DefaultSelectionModel} this
35711         * @param {TreeNode} node the new selection
35712         * @param {TreeNode} node the old selection
35713         */
35714        "beforeselect" : true
35715    });
35716    
35717     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35718 };
35719
35720 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35721     init : function(tree){
35722         this.tree = tree;
35723         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35724         tree.on("click", this.onNodeClick, this);
35725     },
35726     
35727     onNodeClick : function(node, e){
35728         if (e.ctrlKey && this.selNode == node)  {
35729             this.unselect(node);
35730             return;
35731         }
35732         this.select(node);
35733     },
35734     
35735     /**
35736      * Select a node.
35737      * @param {TreeNode} node The node to select
35738      * @return {TreeNode} The selected node
35739      */
35740     select : function(node){
35741         var last = this.selNode;
35742         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35743             if(last){
35744                 last.ui.onSelectedChange(false);
35745             }
35746             this.selNode = node;
35747             node.ui.onSelectedChange(true);
35748             this.fireEvent("selectionchange", this, node, last);
35749         }
35750         return node;
35751     },
35752     
35753     /**
35754      * Deselect a node.
35755      * @param {TreeNode} node The node to unselect
35756      */
35757     unselect : function(node){
35758         if(this.selNode == node){
35759             this.clearSelections();
35760         }    
35761     },
35762     
35763     /**
35764      * Clear all selections
35765      */
35766     clearSelections : function(){
35767         var n = this.selNode;
35768         if(n){
35769             n.ui.onSelectedChange(false);
35770             this.selNode = null;
35771             this.fireEvent("selectionchange", this, null);
35772         }
35773         return n;
35774     },
35775     
35776     /**
35777      * Get the selected node
35778      * @return {TreeNode} The selected node
35779      */
35780     getSelectedNode : function(){
35781         return this.selNode;    
35782     },
35783     
35784     /**
35785      * Returns true if the node is selected
35786      * @param {TreeNode} node The node to check
35787      * @return {Boolean}
35788      */
35789     isSelected : function(node){
35790         return this.selNode == node;  
35791     },
35792
35793     /**
35794      * Selects the node above the selected node in the tree, intelligently walking the nodes
35795      * @return TreeNode The new selection
35796      */
35797     selectPrevious : function(){
35798         var s = this.selNode || this.lastSelNode;
35799         if(!s){
35800             return null;
35801         }
35802         var ps = s.previousSibling;
35803         if(ps){
35804             if(!ps.isExpanded() || ps.childNodes.length < 1){
35805                 return this.select(ps);
35806             } else{
35807                 var lc = ps.lastChild;
35808                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35809                     lc = lc.lastChild;
35810                 }
35811                 return this.select(lc);
35812             }
35813         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35814             return this.select(s.parentNode);
35815         }
35816         return null;
35817     },
35818
35819     /**
35820      * Selects the node above the selected node in the tree, intelligently walking the nodes
35821      * @return TreeNode The new selection
35822      */
35823     selectNext : function(){
35824         var s = this.selNode || this.lastSelNode;
35825         if(!s){
35826             return null;
35827         }
35828         if(s.firstChild && s.isExpanded()){
35829              return this.select(s.firstChild);
35830          }else if(s.nextSibling){
35831              return this.select(s.nextSibling);
35832          }else if(s.parentNode){
35833             var newS = null;
35834             s.parentNode.bubble(function(){
35835                 if(this.nextSibling){
35836                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35837                     return false;
35838                 }
35839             });
35840             return newS;
35841          }
35842         return null;
35843     },
35844
35845     onKeyDown : function(e){
35846         var s = this.selNode || this.lastSelNode;
35847         // undesirable, but required
35848         var sm = this;
35849         if(!s){
35850             return;
35851         }
35852         var k = e.getKey();
35853         switch(k){
35854              case e.DOWN:
35855                  e.stopEvent();
35856                  this.selectNext();
35857              break;
35858              case e.UP:
35859                  e.stopEvent();
35860                  this.selectPrevious();
35861              break;
35862              case e.RIGHT:
35863                  e.preventDefault();
35864                  if(s.hasChildNodes()){
35865                      if(!s.isExpanded()){
35866                          s.expand();
35867                      }else if(s.firstChild){
35868                          this.select(s.firstChild, e);
35869                      }
35870                  }
35871              break;
35872              case e.LEFT:
35873                  e.preventDefault();
35874                  if(s.hasChildNodes() && s.isExpanded()){
35875                      s.collapse();
35876                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35877                      this.select(s.parentNode, e);
35878                  }
35879              break;
35880         };
35881     }
35882 });
35883
35884 /**
35885  * @class Roo.tree.MultiSelectionModel
35886  * @extends Roo.util.Observable
35887  * Multi selection for a TreePanel.
35888  * @param {Object} cfg Configuration
35889  */
35890 Roo.tree.MultiSelectionModel = function(){
35891    this.selNodes = [];
35892    this.selMap = {};
35893    this.addEvents({
35894        /**
35895         * @event selectionchange
35896         * Fires when the selected nodes change
35897         * @param {MultiSelectionModel} this
35898         * @param {Array} nodes Array of the selected nodes
35899         */
35900        "selectionchange" : true
35901    });
35902    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35903    
35904 };
35905
35906 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35907     init : function(tree){
35908         this.tree = tree;
35909         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35910         tree.on("click", this.onNodeClick, this);
35911     },
35912     
35913     onNodeClick : function(node, e){
35914         this.select(node, e, e.ctrlKey);
35915     },
35916     
35917     /**
35918      * Select a node.
35919      * @param {TreeNode} node The node to select
35920      * @param {EventObject} e (optional) An event associated with the selection
35921      * @param {Boolean} keepExisting True to retain existing selections
35922      * @return {TreeNode} The selected node
35923      */
35924     select : function(node, e, keepExisting){
35925         if(keepExisting !== true){
35926             this.clearSelections(true);
35927         }
35928         if(this.isSelected(node)){
35929             this.lastSelNode = node;
35930             return node;
35931         }
35932         this.selNodes.push(node);
35933         this.selMap[node.id] = node;
35934         this.lastSelNode = node;
35935         node.ui.onSelectedChange(true);
35936         this.fireEvent("selectionchange", this, this.selNodes);
35937         return node;
35938     },
35939     
35940     /**
35941      * Deselect a node.
35942      * @param {TreeNode} node The node to unselect
35943      */
35944     unselect : function(node){
35945         if(this.selMap[node.id]){
35946             node.ui.onSelectedChange(false);
35947             var sn = this.selNodes;
35948             var index = -1;
35949             if(sn.indexOf){
35950                 index = sn.indexOf(node);
35951             }else{
35952                 for(var i = 0, len = sn.length; i < len; i++){
35953                     if(sn[i] == node){
35954                         index = i;
35955                         break;
35956                     }
35957                 }
35958             }
35959             if(index != -1){
35960                 this.selNodes.splice(index, 1);
35961             }
35962             delete this.selMap[node.id];
35963             this.fireEvent("selectionchange", this, this.selNodes);
35964         }
35965     },
35966     
35967     /**
35968      * Clear all selections
35969      */
35970     clearSelections : function(suppressEvent){
35971         var sn = this.selNodes;
35972         if(sn.length > 0){
35973             for(var i = 0, len = sn.length; i < len; i++){
35974                 sn[i].ui.onSelectedChange(false);
35975             }
35976             this.selNodes = [];
35977             this.selMap = {};
35978             if(suppressEvent !== true){
35979                 this.fireEvent("selectionchange", this, this.selNodes);
35980             }
35981         }
35982     },
35983     
35984     /**
35985      * Returns true if the node is selected
35986      * @param {TreeNode} node The node to check
35987      * @return {Boolean}
35988      */
35989     isSelected : function(node){
35990         return this.selMap[node.id] ? true : false;  
35991     },
35992     
35993     /**
35994      * Returns an array of the selected nodes
35995      * @return {Array}
35996      */
35997     getSelectedNodes : function(){
35998         return this.selNodes;    
35999     },
36000
36001     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36002
36003     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36004
36005     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36006 });/*
36007  * Based on:
36008  * Ext JS Library 1.1.1
36009  * Copyright(c) 2006-2007, Ext JS, LLC.
36010  *
36011  * Originally Released Under LGPL - original licence link has changed is not relivant.
36012  *
36013  * Fork - LGPL
36014  * <script type="text/javascript">
36015  */
36016  
36017 /**
36018  * @class Roo.tree.TreeNode
36019  * @extends Roo.data.Node
36020  * @cfg {String} text The text for this node
36021  * @cfg {Boolean} expanded true to start the node expanded
36022  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36023  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36024  * @cfg {Boolean} disabled true to start the node disabled
36025  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36026  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36027  * @cfg {String} cls A css class to be added to the node
36028  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36029  * @cfg {String} href URL of the link used for the node (defaults to #)
36030  * @cfg {String} hrefTarget target frame for the link
36031  * @cfg {String} qtip An Ext QuickTip for the node
36032  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36033  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36034  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36035  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36036  * (defaults to undefined with no checkbox rendered)
36037  * @constructor
36038  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36039  */
36040 Roo.tree.TreeNode = function(attributes){
36041     attributes = attributes || {};
36042     if(typeof attributes == "string"){
36043         attributes = {text: attributes};
36044     }
36045     this.childrenRendered = false;
36046     this.rendered = false;
36047     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36048     this.expanded = attributes.expanded === true;
36049     this.isTarget = attributes.isTarget !== false;
36050     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36051     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36052
36053     /**
36054      * Read-only. The text for this node. To change it use setText().
36055      * @type String
36056      */
36057     this.text = attributes.text;
36058     /**
36059      * True if this node is disabled.
36060      * @type Boolean
36061      */
36062     this.disabled = attributes.disabled === true;
36063
36064     this.addEvents({
36065         /**
36066         * @event textchange
36067         * Fires when the text for this node is changed
36068         * @param {Node} this This node
36069         * @param {String} text The new text
36070         * @param {String} oldText The old text
36071         */
36072         "textchange" : true,
36073         /**
36074         * @event beforeexpand
36075         * Fires before this node is expanded, return false to cancel.
36076         * @param {Node} this This node
36077         * @param {Boolean} deep
36078         * @param {Boolean} anim
36079         */
36080         "beforeexpand" : true,
36081         /**
36082         * @event beforecollapse
36083         * Fires before this node is collapsed, return false to cancel.
36084         * @param {Node} this This node
36085         * @param {Boolean} deep
36086         * @param {Boolean} anim
36087         */
36088         "beforecollapse" : true,
36089         /**
36090         * @event expand
36091         * Fires when this node is expanded
36092         * @param {Node} this This node
36093         */
36094         "expand" : true,
36095         /**
36096         * @event disabledchange
36097         * Fires when the disabled status of this node changes
36098         * @param {Node} this This node
36099         * @param {Boolean} disabled
36100         */
36101         "disabledchange" : true,
36102         /**
36103         * @event collapse
36104         * Fires when this node is collapsed
36105         * @param {Node} this This node
36106         */
36107         "collapse" : true,
36108         /**
36109         * @event beforeclick
36110         * Fires before click processing. Return false to cancel the default action.
36111         * @param {Node} this This node
36112         * @param {Roo.EventObject} e The event object
36113         */
36114         "beforeclick":true,
36115         /**
36116         * @event checkchange
36117         * Fires when a node with a checkbox's checked property changes
36118         * @param {Node} this This node
36119         * @param {Boolean} checked
36120         */
36121         "checkchange":true,
36122         /**
36123         * @event click
36124         * Fires when this node is clicked
36125         * @param {Node} this This node
36126         * @param {Roo.EventObject} e The event object
36127         */
36128         "click":true,
36129         /**
36130         * @event dblclick
36131         * Fires when this node is double clicked
36132         * @param {Node} this This node
36133         * @param {Roo.EventObject} e The event object
36134         */
36135         "dblclick":true,
36136         /**
36137         * @event contextmenu
36138         * Fires when this node is right clicked
36139         * @param {Node} this This node
36140         * @param {Roo.EventObject} e The event object
36141         */
36142         "contextmenu":true,
36143         /**
36144         * @event beforechildrenrendered
36145         * Fires right before the child nodes for this node are rendered
36146         * @param {Node} this This node
36147         */
36148         "beforechildrenrendered":true
36149     });
36150
36151     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36152
36153     /**
36154      * Read-only. The UI for this node
36155      * @type TreeNodeUI
36156      */
36157     this.ui = new uiClass(this);
36158     
36159     // finally support items[]
36160     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36161         return;
36162     }
36163     
36164     
36165     Roo.each(this.attributes.items, function(c) {
36166         this.appendChild(Roo.factory(c,Roo.Tree));
36167     }, this);
36168     delete this.attributes.items;
36169     
36170     
36171     
36172 };
36173 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36174     preventHScroll: true,
36175     /**
36176      * Returns true if this node is expanded
36177      * @return {Boolean}
36178      */
36179     isExpanded : function(){
36180         return this.expanded;
36181     },
36182
36183     /**
36184      * Returns the UI object for this node
36185      * @return {TreeNodeUI}
36186      */
36187     getUI : function(){
36188         return this.ui;
36189     },
36190
36191     // private override
36192     setFirstChild : function(node){
36193         var of = this.firstChild;
36194         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36195         if(this.childrenRendered && of && node != of){
36196             of.renderIndent(true, true);
36197         }
36198         if(this.rendered){
36199             this.renderIndent(true, true);
36200         }
36201     },
36202
36203     // private override
36204     setLastChild : function(node){
36205         var ol = this.lastChild;
36206         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36207         if(this.childrenRendered && ol && node != ol){
36208             ol.renderIndent(true, true);
36209         }
36210         if(this.rendered){
36211             this.renderIndent(true, true);
36212         }
36213     },
36214
36215     // these methods are overridden to provide lazy rendering support
36216     // private override
36217     appendChild : function()
36218     {
36219         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36220         if(node && this.childrenRendered){
36221             node.render();
36222         }
36223         this.ui.updateExpandIcon();
36224         return node;
36225     },
36226
36227     // private override
36228     removeChild : function(node){
36229         this.ownerTree.getSelectionModel().unselect(node);
36230         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36231         // if it's been rendered remove dom node
36232         if(this.childrenRendered){
36233             node.ui.remove();
36234         }
36235         if(this.childNodes.length < 1){
36236             this.collapse(false, false);
36237         }else{
36238             this.ui.updateExpandIcon();
36239         }
36240         if(!this.firstChild) {
36241             this.childrenRendered = false;
36242         }
36243         return node;
36244     },
36245
36246     // private override
36247     insertBefore : function(node, refNode){
36248         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36249         if(newNode && refNode && this.childrenRendered){
36250             node.render();
36251         }
36252         this.ui.updateExpandIcon();
36253         return newNode;
36254     },
36255
36256     /**
36257      * Sets the text for this node
36258      * @param {String} text
36259      */
36260     setText : function(text){
36261         var oldText = this.text;
36262         this.text = text;
36263         this.attributes.text = text;
36264         if(this.rendered){ // event without subscribing
36265             this.ui.onTextChange(this, text, oldText);
36266         }
36267         this.fireEvent("textchange", this, text, oldText);
36268     },
36269
36270     /**
36271      * Triggers selection of this node
36272      */
36273     select : function(){
36274         this.getOwnerTree().getSelectionModel().select(this);
36275     },
36276
36277     /**
36278      * Triggers deselection of this node
36279      */
36280     unselect : function(){
36281         this.getOwnerTree().getSelectionModel().unselect(this);
36282     },
36283
36284     /**
36285      * Returns true if this node is selected
36286      * @return {Boolean}
36287      */
36288     isSelected : function(){
36289         return this.getOwnerTree().getSelectionModel().isSelected(this);
36290     },
36291
36292     /**
36293      * Expand this node.
36294      * @param {Boolean} deep (optional) True to expand all children as well
36295      * @param {Boolean} anim (optional) false to cancel the default animation
36296      * @param {Function} callback (optional) A callback to be called when
36297      * expanding this node completes (does not wait for deep expand to complete).
36298      * Called with 1 parameter, this node.
36299      */
36300     expand : function(deep, anim, callback){
36301         if(!this.expanded){
36302             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36303                 return;
36304             }
36305             if(!this.childrenRendered){
36306                 this.renderChildren();
36307             }
36308             this.expanded = true;
36309             
36310             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36311                 this.ui.animExpand(function(){
36312                     this.fireEvent("expand", this);
36313                     if(typeof callback == "function"){
36314                         callback(this);
36315                     }
36316                     if(deep === true){
36317                         this.expandChildNodes(true);
36318                     }
36319                 }.createDelegate(this));
36320                 return;
36321             }else{
36322                 this.ui.expand();
36323                 this.fireEvent("expand", this);
36324                 if(typeof callback == "function"){
36325                     callback(this);
36326                 }
36327             }
36328         }else{
36329            if(typeof callback == "function"){
36330                callback(this);
36331            }
36332         }
36333         if(deep === true){
36334             this.expandChildNodes(true);
36335         }
36336     },
36337
36338     isHiddenRoot : function(){
36339         return this.isRoot && !this.getOwnerTree().rootVisible;
36340     },
36341
36342     /**
36343      * Collapse this node.
36344      * @param {Boolean} deep (optional) True to collapse all children as well
36345      * @param {Boolean} anim (optional) false to cancel the default animation
36346      */
36347     collapse : function(deep, anim){
36348         if(this.expanded && !this.isHiddenRoot()){
36349             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36350                 return;
36351             }
36352             this.expanded = false;
36353             if((this.getOwnerTree().animate && anim !== false) || anim){
36354                 this.ui.animCollapse(function(){
36355                     this.fireEvent("collapse", this);
36356                     if(deep === true){
36357                         this.collapseChildNodes(true);
36358                     }
36359                 }.createDelegate(this));
36360                 return;
36361             }else{
36362                 this.ui.collapse();
36363                 this.fireEvent("collapse", this);
36364             }
36365         }
36366         if(deep === true){
36367             var cs = this.childNodes;
36368             for(var i = 0, len = cs.length; i < len; i++) {
36369                 cs[i].collapse(true, false);
36370             }
36371         }
36372     },
36373
36374     // private
36375     delayedExpand : function(delay){
36376         if(!this.expandProcId){
36377             this.expandProcId = this.expand.defer(delay, this);
36378         }
36379     },
36380
36381     // private
36382     cancelExpand : function(){
36383         if(this.expandProcId){
36384             clearTimeout(this.expandProcId);
36385         }
36386         this.expandProcId = false;
36387     },
36388
36389     /**
36390      * Toggles expanded/collapsed state of the node
36391      */
36392     toggle : function(){
36393         if(this.expanded){
36394             this.collapse();
36395         }else{
36396             this.expand();
36397         }
36398     },
36399
36400     /**
36401      * Ensures all parent nodes are expanded
36402      */
36403     ensureVisible : function(callback){
36404         var tree = this.getOwnerTree();
36405         tree.expandPath(this.parentNode.getPath(), false, function(){
36406             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36407             Roo.callback(callback);
36408         }.createDelegate(this));
36409     },
36410
36411     /**
36412      * Expand all child nodes
36413      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36414      */
36415     expandChildNodes : function(deep){
36416         var cs = this.childNodes;
36417         for(var i = 0, len = cs.length; i < len; i++) {
36418                 cs[i].expand(deep);
36419         }
36420     },
36421
36422     /**
36423      * Collapse all child nodes
36424      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36425      */
36426     collapseChildNodes : function(deep){
36427         var cs = this.childNodes;
36428         for(var i = 0, len = cs.length; i < len; i++) {
36429                 cs[i].collapse(deep);
36430         }
36431     },
36432
36433     /**
36434      * Disables this node
36435      */
36436     disable : function(){
36437         this.disabled = true;
36438         this.unselect();
36439         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36440             this.ui.onDisableChange(this, true);
36441         }
36442         this.fireEvent("disabledchange", this, true);
36443     },
36444
36445     /**
36446      * Enables this node
36447      */
36448     enable : function(){
36449         this.disabled = false;
36450         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36451             this.ui.onDisableChange(this, false);
36452         }
36453         this.fireEvent("disabledchange", this, false);
36454     },
36455
36456     // private
36457     renderChildren : function(suppressEvent){
36458         if(suppressEvent !== false){
36459             this.fireEvent("beforechildrenrendered", this);
36460         }
36461         var cs = this.childNodes;
36462         for(var i = 0, len = cs.length; i < len; i++){
36463             cs[i].render(true);
36464         }
36465         this.childrenRendered = true;
36466     },
36467
36468     // private
36469     sort : function(fn, scope){
36470         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36471         if(this.childrenRendered){
36472             var cs = this.childNodes;
36473             for(var i = 0, len = cs.length; i < len; i++){
36474                 cs[i].render(true);
36475             }
36476         }
36477     },
36478
36479     // private
36480     render : function(bulkRender){
36481         this.ui.render(bulkRender);
36482         if(!this.rendered){
36483             this.rendered = true;
36484             if(this.expanded){
36485                 this.expanded = false;
36486                 this.expand(false, false);
36487             }
36488         }
36489     },
36490
36491     // private
36492     renderIndent : function(deep, refresh){
36493         if(refresh){
36494             this.ui.childIndent = null;
36495         }
36496         this.ui.renderIndent();
36497         if(deep === true && this.childrenRendered){
36498             var cs = this.childNodes;
36499             for(var i = 0, len = cs.length; i < len; i++){
36500                 cs[i].renderIndent(true, refresh);
36501             }
36502         }
36503     }
36504 });/*
36505  * Based on:
36506  * Ext JS Library 1.1.1
36507  * Copyright(c) 2006-2007, Ext JS, LLC.
36508  *
36509  * Originally Released Under LGPL - original licence link has changed is not relivant.
36510  *
36511  * Fork - LGPL
36512  * <script type="text/javascript">
36513  */
36514  
36515 /**
36516  * @class Roo.tree.AsyncTreeNode
36517  * @extends Roo.tree.TreeNode
36518  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36519  * @constructor
36520  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36521  */
36522  Roo.tree.AsyncTreeNode = function(config){
36523     this.loaded = false;
36524     this.loading = false;
36525     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36526     /**
36527     * @event beforeload
36528     * Fires before this node is loaded, return false to cancel
36529     * @param {Node} this This node
36530     */
36531     this.addEvents({'beforeload':true, 'load': true});
36532     /**
36533     * @event load
36534     * Fires when this node is loaded
36535     * @param {Node} this This node
36536     */
36537     /**
36538      * The loader used by this node (defaults to using the tree's defined loader)
36539      * @type TreeLoader
36540      * @property loader
36541      */
36542 };
36543 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36544     expand : function(deep, anim, callback){
36545         if(this.loading){ // if an async load is already running, waiting til it's done
36546             var timer;
36547             var f = function(){
36548                 if(!this.loading){ // done loading
36549                     clearInterval(timer);
36550                     this.expand(deep, anim, callback);
36551                 }
36552             }.createDelegate(this);
36553             timer = setInterval(f, 200);
36554             return;
36555         }
36556         if(!this.loaded){
36557             if(this.fireEvent("beforeload", this) === false){
36558                 return;
36559             }
36560             this.loading = true;
36561             this.ui.beforeLoad(this);
36562             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36563             if(loader){
36564                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36565                 return;
36566             }
36567         }
36568         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36569     },
36570     
36571     /**
36572      * Returns true if this node is currently loading
36573      * @return {Boolean}
36574      */
36575     isLoading : function(){
36576         return this.loading;  
36577     },
36578     
36579     loadComplete : function(deep, anim, callback){
36580         this.loading = false;
36581         this.loaded = true;
36582         this.ui.afterLoad(this);
36583         this.fireEvent("load", this);
36584         this.expand(deep, anim, callback);
36585     },
36586     
36587     /**
36588      * Returns true if this node has been loaded
36589      * @return {Boolean}
36590      */
36591     isLoaded : function(){
36592         return this.loaded;
36593     },
36594     
36595     hasChildNodes : function(){
36596         if(!this.isLeaf() && !this.loaded){
36597             return true;
36598         }else{
36599             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36600         }
36601     },
36602
36603     /**
36604      * Trigger a reload for this node
36605      * @param {Function} callback
36606      */
36607     reload : function(callback){
36608         this.collapse(false, false);
36609         while(this.firstChild){
36610             this.removeChild(this.firstChild);
36611         }
36612         this.childrenRendered = false;
36613         this.loaded = false;
36614         if(this.isHiddenRoot()){
36615             this.expanded = false;
36616         }
36617         this.expand(false, false, callback);
36618     }
36619 });/*
36620  * Based on:
36621  * Ext JS Library 1.1.1
36622  * Copyright(c) 2006-2007, Ext JS, LLC.
36623  *
36624  * Originally Released Under LGPL - original licence link has changed is not relivant.
36625  *
36626  * Fork - LGPL
36627  * <script type="text/javascript">
36628  */
36629  
36630 /**
36631  * @class Roo.tree.TreeNodeUI
36632  * @constructor
36633  * @param {Object} node The node to render
36634  * The TreeNode UI implementation is separate from the
36635  * tree implementation. Unless you are customizing the tree UI,
36636  * you should never have to use this directly.
36637  */
36638 Roo.tree.TreeNodeUI = function(node){
36639     this.node = node;
36640     this.rendered = false;
36641     this.animating = false;
36642     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36643 };
36644
36645 Roo.tree.TreeNodeUI.prototype = {
36646     removeChild : function(node){
36647         if(this.rendered){
36648             this.ctNode.removeChild(node.ui.getEl());
36649         }
36650     },
36651
36652     beforeLoad : function(){
36653          this.addClass("x-tree-node-loading");
36654     },
36655
36656     afterLoad : function(){
36657          this.removeClass("x-tree-node-loading");
36658     },
36659
36660     onTextChange : function(node, text, oldText){
36661         if(this.rendered){
36662             this.textNode.innerHTML = text;
36663         }
36664     },
36665
36666     onDisableChange : function(node, state){
36667         this.disabled = state;
36668         if(state){
36669             this.addClass("x-tree-node-disabled");
36670         }else{
36671             this.removeClass("x-tree-node-disabled");
36672         }
36673     },
36674
36675     onSelectedChange : function(state){
36676         if(state){
36677             this.focus();
36678             this.addClass("x-tree-selected");
36679         }else{
36680             //this.blur();
36681             this.removeClass("x-tree-selected");
36682         }
36683     },
36684
36685     onMove : function(tree, node, oldParent, newParent, index, refNode){
36686         this.childIndent = null;
36687         if(this.rendered){
36688             var targetNode = newParent.ui.getContainer();
36689             if(!targetNode){//target not rendered
36690                 this.holder = document.createElement("div");
36691                 this.holder.appendChild(this.wrap);
36692                 return;
36693             }
36694             var insertBefore = refNode ? refNode.ui.getEl() : null;
36695             if(insertBefore){
36696                 targetNode.insertBefore(this.wrap, insertBefore);
36697             }else{
36698                 targetNode.appendChild(this.wrap);
36699             }
36700             this.node.renderIndent(true);
36701         }
36702     },
36703
36704     addClass : function(cls){
36705         if(this.elNode){
36706             Roo.fly(this.elNode).addClass(cls);
36707         }
36708     },
36709
36710     removeClass : function(cls){
36711         if(this.elNode){
36712             Roo.fly(this.elNode).removeClass(cls);
36713         }
36714     },
36715
36716     remove : function(){
36717         if(this.rendered){
36718             this.holder = document.createElement("div");
36719             this.holder.appendChild(this.wrap);
36720         }
36721     },
36722
36723     fireEvent : function(){
36724         return this.node.fireEvent.apply(this.node, arguments);
36725     },
36726
36727     initEvents : function(){
36728         this.node.on("move", this.onMove, this);
36729         var E = Roo.EventManager;
36730         var a = this.anchor;
36731
36732         var el = Roo.fly(a, '_treeui');
36733
36734         if(Roo.isOpera){ // opera render bug ignores the CSS
36735             el.setStyle("text-decoration", "none");
36736         }
36737
36738         el.on("click", this.onClick, this);
36739         el.on("dblclick", this.onDblClick, this);
36740
36741         if(this.checkbox){
36742             Roo.EventManager.on(this.checkbox,
36743                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36744         }
36745
36746         el.on("contextmenu", this.onContextMenu, this);
36747
36748         var icon = Roo.fly(this.iconNode);
36749         icon.on("click", this.onClick, this);
36750         icon.on("dblclick", this.onDblClick, this);
36751         icon.on("contextmenu", this.onContextMenu, this);
36752         E.on(this.ecNode, "click", this.ecClick, this, true);
36753
36754         if(this.node.disabled){
36755             this.addClass("x-tree-node-disabled");
36756         }
36757         if(this.node.hidden){
36758             this.addClass("x-tree-node-disabled");
36759         }
36760         var ot = this.node.getOwnerTree();
36761         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36762         if(dd && (!this.node.isRoot || ot.rootVisible)){
36763             Roo.dd.Registry.register(this.elNode, {
36764                 node: this.node,
36765                 handles: this.getDDHandles(),
36766                 isHandle: false
36767             });
36768         }
36769     },
36770
36771     getDDHandles : function(){
36772         return [this.iconNode, this.textNode];
36773     },
36774
36775     hide : function(){
36776         if(this.rendered){
36777             this.wrap.style.display = "none";
36778         }
36779     },
36780
36781     show : function(){
36782         if(this.rendered){
36783             this.wrap.style.display = "";
36784         }
36785     },
36786
36787     onContextMenu : function(e){
36788         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36789             e.preventDefault();
36790             this.focus();
36791             this.fireEvent("contextmenu", this.node, e);
36792         }
36793     },
36794
36795     onClick : function(e){
36796         if(this.dropping){
36797             e.stopEvent();
36798             return;
36799         }
36800         if(this.fireEvent("beforeclick", this.node, e) !== false){
36801             if(!this.disabled && this.node.attributes.href){
36802                 this.fireEvent("click", this.node, e);
36803                 return;
36804             }
36805             e.preventDefault();
36806             if(this.disabled){
36807                 return;
36808             }
36809
36810             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36811                 this.node.toggle();
36812             }
36813
36814             this.fireEvent("click", this.node, e);
36815         }else{
36816             e.stopEvent();
36817         }
36818     },
36819
36820     onDblClick : function(e){
36821         e.preventDefault();
36822         if(this.disabled){
36823             return;
36824         }
36825         if(this.checkbox){
36826             this.toggleCheck();
36827         }
36828         if(!this.animating && this.node.hasChildNodes()){
36829             this.node.toggle();
36830         }
36831         this.fireEvent("dblclick", this.node, e);
36832     },
36833
36834     onCheckChange : function(){
36835         var checked = this.checkbox.checked;
36836         this.node.attributes.checked = checked;
36837         this.fireEvent('checkchange', this.node, checked);
36838     },
36839
36840     ecClick : function(e){
36841         if(!this.animating && this.node.hasChildNodes()){
36842             this.node.toggle();
36843         }
36844     },
36845
36846     startDrop : function(){
36847         this.dropping = true;
36848     },
36849
36850     // delayed drop so the click event doesn't get fired on a drop
36851     endDrop : function(){
36852        setTimeout(function(){
36853            this.dropping = false;
36854        }.createDelegate(this), 50);
36855     },
36856
36857     expand : function(){
36858         this.updateExpandIcon();
36859         this.ctNode.style.display = "";
36860     },
36861
36862     focus : function(){
36863         if(!this.node.preventHScroll){
36864             try{this.anchor.focus();
36865             }catch(e){}
36866         }else if(!Roo.isIE){
36867             try{
36868                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36869                 var l = noscroll.scrollLeft;
36870                 this.anchor.focus();
36871                 noscroll.scrollLeft = l;
36872             }catch(e){}
36873         }
36874     },
36875
36876     toggleCheck : function(value){
36877         var cb = this.checkbox;
36878         if(cb){
36879             cb.checked = (value === undefined ? !cb.checked : value);
36880         }
36881     },
36882
36883     blur : function(){
36884         try{
36885             this.anchor.blur();
36886         }catch(e){}
36887     },
36888
36889     animExpand : function(callback){
36890         var ct = Roo.get(this.ctNode);
36891         ct.stopFx();
36892         if(!this.node.hasChildNodes()){
36893             this.updateExpandIcon();
36894             this.ctNode.style.display = "";
36895             Roo.callback(callback);
36896             return;
36897         }
36898         this.animating = true;
36899         this.updateExpandIcon();
36900
36901         ct.slideIn('t', {
36902            callback : function(){
36903                this.animating = false;
36904                Roo.callback(callback);
36905             },
36906             scope: this,
36907             duration: this.node.ownerTree.duration || .25
36908         });
36909     },
36910
36911     highlight : function(){
36912         var tree = this.node.getOwnerTree();
36913         Roo.fly(this.wrap).highlight(
36914             tree.hlColor || "C3DAF9",
36915             {endColor: tree.hlBaseColor}
36916         );
36917     },
36918
36919     collapse : function(){
36920         this.updateExpandIcon();
36921         this.ctNode.style.display = "none";
36922     },
36923
36924     animCollapse : function(callback){
36925         var ct = Roo.get(this.ctNode);
36926         ct.enableDisplayMode('block');
36927         ct.stopFx();
36928
36929         this.animating = true;
36930         this.updateExpandIcon();
36931
36932         ct.slideOut('t', {
36933             callback : function(){
36934                this.animating = false;
36935                Roo.callback(callback);
36936             },
36937             scope: this,
36938             duration: this.node.ownerTree.duration || .25
36939         });
36940     },
36941
36942     getContainer : function(){
36943         return this.ctNode;
36944     },
36945
36946     getEl : function(){
36947         return this.wrap;
36948     },
36949
36950     appendDDGhost : function(ghostNode){
36951         ghostNode.appendChild(this.elNode.cloneNode(true));
36952     },
36953
36954     getDDRepairXY : function(){
36955         return Roo.lib.Dom.getXY(this.iconNode);
36956     },
36957
36958     onRender : function(){
36959         this.render();
36960     },
36961
36962     render : function(bulkRender){
36963         var n = this.node, a = n.attributes;
36964         var targetNode = n.parentNode ?
36965               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36966
36967         if(!this.rendered){
36968             this.rendered = true;
36969
36970             this.renderElements(n, a, targetNode, bulkRender);
36971
36972             if(a.qtip){
36973                if(this.textNode.setAttributeNS){
36974                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36975                    if(a.qtipTitle){
36976                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36977                    }
36978                }else{
36979                    this.textNode.setAttribute("ext:qtip", a.qtip);
36980                    if(a.qtipTitle){
36981                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36982                    }
36983                }
36984             }else if(a.qtipCfg){
36985                 a.qtipCfg.target = Roo.id(this.textNode);
36986                 Roo.QuickTips.register(a.qtipCfg);
36987             }
36988             this.initEvents();
36989             if(!this.node.expanded){
36990                 this.updateExpandIcon();
36991             }
36992         }else{
36993             if(bulkRender === true) {
36994                 targetNode.appendChild(this.wrap);
36995             }
36996         }
36997     },
36998
36999     renderElements : function(n, a, targetNode, bulkRender)
37000     {
37001         // add some indent caching, this helps performance when rendering a large tree
37002         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37003         var t = n.getOwnerTree();
37004         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37005         if (typeof(n.attributes.html) != 'undefined') {
37006             txt = n.attributes.html;
37007         }
37008         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37009         var cb = typeof a.checked == 'boolean';
37010         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37011         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37012             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37013             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37014             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37015             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37016             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37017              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37018                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37019             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37020             "</li>"];
37021
37022         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37023             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37024                                 n.nextSibling.ui.getEl(), buf.join(""));
37025         }else{
37026             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37027         }
37028
37029         this.elNode = this.wrap.childNodes[0];
37030         this.ctNode = this.wrap.childNodes[1];
37031         var cs = this.elNode.childNodes;
37032         this.indentNode = cs[0];
37033         this.ecNode = cs[1];
37034         this.iconNode = cs[2];
37035         var index = 3;
37036         if(cb){
37037             this.checkbox = cs[3];
37038             index++;
37039         }
37040         this.anchor = cs[index];
37041         this.textNode = cs[index].firstChild;
37042     },
37043
37044     getAnchor : function(){
37045         return this.anchor;
37046     },
37047
37048     getTextEl : function(){
37049         return this.textNode;
37050     },
37051
37052     getIconEl : function(){
37053         return this.iconNode;
37054     },
37055
37056     isChecked : function(){
37057         return this.checkbox ? this.checkbox.checked : false;
37058     },
37059
37060     updateExpandIcon : function(){
37061         if(this.rendered){
37062             var n = this.node, c1, c2;
37063             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37064             var hasChild = n.hasChildNodes();
37065             if(hasChild){
37066                 if(n.expanded){
37067                     cls += "-minus";
37068                     c1 = "x-tree-node-collapsed";
37069                     c2 = "x-tree-node-expanded";
37070                 }else{
37071                     cls += "-plus";
37072                     c1 = "x-tree-node-expanded";
37073                     c2 = "x-tree-node-collapsed";
37074                 }
37075                 if(this.wasLeaf){
37076                     this.removeClass("x-tree-node-leaf");
37077                     this.wasLeaf = false;
37078                 }
37079                 if(this.c1 != c1 || this.c2 != c2){
37080                     Roo.fly(this.elNode).replaceClass(c1, c2);
37081                     this.c1 = c1; this.c2 = c2;
37082                 }
37083             }else{
37084                 // this changes non-leafs into leafs if they have no children.
37085                 // it's not very rational behaviour..
37086                 
37087                 if(!this.wasLeaf && this.node.leaf){
37088                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37089                     delete this.c1;
37090                     delete this.c2;
37091                     this.wasLeaf = true;
37092                 }
37093             }
37094             var ecc = "x-tree-ec-icon "+cls;
37095             if(this.ecc != ecc){
37096                 this.ecNode.className = ecc;
37097                 this.ecc = ecc;
37098             }
37099         }
37100     },
37101
37102     getChildIndent : function(){
37103         if(!this.childIndent){
37104             var buf = [];
37105             var p = this.node;
37106             while(p){
37107                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37108                     if(!p.isLast()) {
37109                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37110                     } else {
37111                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37112                     }
37113                 }
37114                 p = p.parentNode;
37115             }
37116             this.childIndent = buf.join("");
37117         }
37118         return this.childIndent;
37119     },
37120
37121     renderIndent : function(){
37122         if(this.rendered){
37123             var indent = "";
37124             var p = this.node.parentNode;
37125             if(p){
37126                 indent = p.ui.getChildIndent();
37127             }
37128             if(this.indentMarkup != indent){ // don't rerender if not required
37129                 this.indentNode.innerHTML = indent;
37130                 this.indentMarkup = indent;
37131             }
37132             this.updateExpandIcon();
37133         }
37134     }
37135 };
37136
37137 Roo.tree.RootTreeNodeUI = function(){
37138     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37139 };
37140 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37141     render : function(){
37142         if(!this.rendered){
37143             var targetNode = this.node.ownerTree.innerCt.dom;
37144             this.node.expanded = true;
37145             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37146             this.wrap = this.ctNode = targetNode.firstChild;
37147         }
37148     },
37149     collapse : function(){
37150     },
37151     expand : function(){
37152     }
37153 });/*
37154  * Based on:
37155  * Ext JS Library 1.1.1
37156  * Copyright(c) 2006-2007, Ext JS, LLC.
37157  *
37158  * Originally Released Under LGPL - original licence link has changed is not relivant.
37159  *
37160  * Fork - LGPL
37161  * <script type="text/javascript">
37162  */
37163 /**
37164  * @class Roo.tree.TreeLoader
37165  * @extends Roo.util.Observable
37166  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37167  * nodes from a specified URL. The response must be a javascript Array definition
37168  * who's elements are node definition objects. eg:
37169  * <pre><code>
37170 {  success : true,
37171    data :      [
37172    
37173     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37174     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37175     ]
37176 }
37177
37178
37179 </code></pre>
37180  * <br><br>
37181  * The old style respose with just an array is still supported, but not recommended.
37182  * <br><br>
37183  *
37184  * A server request is sent, and child nodes are loaded only when a node is expanded.
37185  * The loading node's id is passed to the server under the parameter name "node" to
37186  * enable the server to produce the correct child nodes.
37187  * <br><br>
37188  * To pass extra parameters, an event handler may be attached to the "beforeload"
37189  * event, and the parameters specified in the TreeLoader's baseParams property:
37190  * <pre><code>
37191     myTreeLoader.on("beforeload", function(treeLoader, node) {
37192         this.baseParams.category = node.attributes.category;
37193     }, this);
37194     
37195 </code></pre>
37196  *
37197  * This would pass an HTTP parameter called "category" to the server containing
37198  * the value of the Node's "category" attribute.
37199  * @constructor
37200  * Creates a new Treeloader.
37201  * @param {Object} config A config object containing config properties.
37202  */
37203 Roo.tree.TreeLoader = function(config){
37204     this.baseParams = {};
37205     this.requestMethod = "POST";
37206     Roo.apply(this, config);
37207
37208     this.addEvents({
37209     
37210         /**
37211          * @event beforeload
37212          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37213          * @param {Object} This TreeLoader object.
37214          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37215          * @param {Object} callback The callback function specified in the {@link #load} call.
37216          */
37217         beforeload : true,
37218         /**
37219          * @event load
37220          * Fires when the node has been successfuly loaded.
37221          * @param {Object} This TreeLoader object.
37222          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37223          * @param {Object} response The response object containing the data from the server.
37224          */
37225         load : true,
37226         /**
37227          * @event loadexception
37228          * Fires if the network request failed.
37229          * @param {Object} This TreeLoader object.
37230          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37231          * @param {Object} response The response object containing the data from the server.
37232          */
37233         loadexception : true,
37234         /**
37235          * @event create
37236          * Fires before a node is created, enabling you to return custom Node types 
37237          * @param {Object} This TreeLoader object.
37238          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37239          */
37240         create : true
37241     });
37242
37243     Roo.tree.TreeLoader.superclass.constructor.call(this);
37244 };
37245
37246 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37247     /**
37248     * @cfg {String} dataUrl The URL from which to request a Json string which
37249     * specifies an array of node definition object representing the child nodes
37250     * to be loaded.
37251     */
37252     /**
37253     * @cfg {String} requestMethod either GET or POST
37254     * defaults to POST (due to BC)
37255     * to be loaded.
37256     */
37257     /**
37258     * @cfg {Object} baseParams (optional) An object containing properties which
37259     * specify HTTP parameters to be passed to each request for child nodes.
37260     */
37261     /**
37262     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37263     * created by this loader. If the attributes sent by the server have an attribute in this object,
37264     * they take priority.
37265     */
37266     /**
37267     * @cfg {Object} uiProviders (optional) An object containing properties which
37268     * 
37269     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37270     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37271     * <i>uiProvider</i> attribute of a returned child node is a string rather
37272     * than a reference to a TreeNodeUI implementation, this that string value
37273     * is used as a property name in the uiProviders object. You can define the provider named
37274     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37275     */
37276     uiProviders : {},
37277
37278     /**
37279     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37280     * child nodes before loading.
37281     */
37282     clearOnLoad : true,
37283
37284     /**
37285     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37286     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37287     * Grid query { data : [ .....] }
37288     */
37289     
37290     root : false,
37291      /**
37292     * @cfg {String} queryParam (optional) 
37293     * Name of the query as it will be passed on the querystring (defaults to 'node')
37294     * eg. the request will be ?node=[id]
37295     */
37296     
37297     
37298     queryParam: false,
37299     
37300     /**
37301      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37302      * This is called automatically when a node is expanded, but may be used to reload
37303      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37304      * @param {Roo.tree.TreeNode} node
37305      * @param {Function} callback
37306      */
37307     load : function(node, callback){
37308         if(this.clearOnLoad){
37309             while(node.firstChild){
37310                 node.removeChild(node.firstChild);
37311             }
37312         }
37313         if(node.attributes.children){ // preloaded json children
37314             var cs = node.attributes.children;
37315             for(var i = 0, len = cs.length; i < len; i++){
37316                 node.appendChild(this.createNode(cs[i]));
37317             }
37318             if(typeof callback == "function"){
37319                 callback();
37320             }
37321         }else if(this.dataUrl){
37322             this.requestData(node, callback);
37323         }
37324     },
37325
37326     getParams: function(node){
37327         var buf = [], bp = this.baseParams;
37328         for(var key in bp){
37329             if(typeof bp[key] != "function"){
37330                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37331             }
37332         }
37333         var n = this.queryParam === false ? 'node' : this.queryParam;
37334         buf.push(n + "=", encodeURIComponent(node.id));
37335         return buf.join("");
37336     },
37337
37338     requestData : function(node, callback){
37339         if(this.fireEvent("beforeload", this, node, callback) !== false){
37340             this.transId = Roo.Ajax.request({
37341                 method:this.requestMethod,
37342                 url: this.dataUrl||this.url,
37343                 success: this.handleResponse,
37344                 failure: this.handleFailure,
37345                 scope: this,
37346                 argument: {callback: callback, node: node},
37347                 params: this.getParams(node)
37348             });
37349         }else{
37350             // if the load is cancelled, make sure we notify
37351             // the node that we are done
37352             if(typeof callback == "function"){
37353                 callback();
37354             }
37355         }
37356     },
37357
37358     isLoading : function(){
37359         return this.transId ? true : false;
37360     },
37361
37362     abort : function(){
37363         if(this.isLoading()){
37364             Roo.Ajax.abort(this.transId);
37365         }
37366     },
37367
37368     // private
37369     createNode : function(attr)
37370     {
37371         // apply baseAttrs, nice idea Corey!
37372         if(this.baseAttrs){
37373             Roo.applyIf(attr, this.baseAttrs);
37374         }
37375         if(this.applyLoader !== false){
37376             attr.loader = this;
37377         }
37378         // uiProvider = depreciated..
37379         
37380         if(typeof(attr.uiProvider) == 'string'){
37381            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37382                 /**  eval:var:attr */ eval(attr.uiProvider);
37383         }
37384         if(typeof(this.uiProviders['default']) != 'undefined') {
37385             attr.uiProvider = this.uiProviders['default'];
37386         }
37387         
37388         this.fireEvent('create', this, attr);
37389         
37390         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37391         return(attr.leaf ?
37392                         new Roo.tree.TreeNode(attr) :
37393                         new Roo.tree.AsyncTreeNode(attr));
37394     },
37395
37396     processResponse : function(response, node, callback)
37397     {
37398         var json = response.responseText;
37399         try {
37400             
37401             var o = Roo.decode(json);
37402             
37403             if (this.root === false && typeof(o.success) != undefined) {
37404                 this.root = 'data'; // the default behaviour for list like data..
37405                 }
37406                 
37407             if (this.root !== false &&  !o.success) {
37408                 // it's a failure condition.
37409                 var a = response.argument;
37410                 this.fireEvent("loadexception", this, a.node, response);
37411                 Roo.log("Load failed - should have a handler really");
37412                 return;
37413             }
37414             
37415             
37416             
37417             if (this.root !== false) {
37418                  o = o[this.root];
37419             }
37420             
37421             for(var i = 0, len = o.length; i < len; i++){
37422                 var n = this.createNode(o[i]);
37423                 if(n){
37424                     node.appendChild(n);
37425                 }
37426             }
37427             if(typeof callback == "function"){
37428                 callback(this, node);
37429             }
37430         }catch(e){
37431             this.handleFailure(response);
37432         }
37433     },
37434
37435     handleResponse : function(response){
37436         this.transId = false;
37437         var a = response.argument;
37438         this.processResponse(response, a.node, a.callback);
37439         this.fireEvent("load", this, a.node, response);
37440     },
37441
37442     handleFailure : function(response)
37443     {
37444         // should handle failure better..
37445         this.transId = false;
37446         var a = response.argument;
37447         this.fireEvent("loadexception", this, a.node, response);
37448         if(typeof a.callback == "function"){
37449             a.callback(this, a.node);
37450         }
37451     }
37452 });/*
37453  * Based on:
37454  * Ext JS Library 1.1.1
37455  * Copyright(c) 2006-2007, Ext JS, LLC.
37456  *
37457  * Originally Released Under LGPL - original licence link has changed is not relivant.
37458  *
37459  * Fork - LGPL
37460  * <script type="text/javascript">
37461  */
37462
37463 /**
37464 * @class Roo.tree.TreeFilter
37465 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37466 * @param {TreePanel} tree
37467 * @param {Object} config (optional)
37468  */
37469 Roo.tree.TreeFilter = function(tree, config){
37470     this.tree = tree;
37471     this.filtered = {};
37472     Roo.apply(this, config);
37473 };
37474
37475 Roo.tree.TreeFilter.prototype = {
37476     clearBlank:false,
37477     reverse:false,
37478     autoClear:false,
37479     remove:false,
37480
37481      /**
37482      * Filter the data by a specific attribute.
37483      * @param {String/RegExp} value Either string that the attribute value
37484      * should start with or a RegExp to test against the attribute
37485      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37486      * @param {TreeNode} startNode (optional) The node to start the filter at.
37487      */
37488     filter : function(value, attr, startNode){
37489         attr = attr || "text";
37490         var f;
37491         if(typeof value == "string"){
37492             var vlen = value.length;
37493             // auto clear empty filter
37494             if(vlen == 0 && this.clearBlank){
37495                 this.clear();
37496                 return;
37497             }
37498             value = value.toLowerCase();
37499             f = function(n){
37500                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37501             };
37502         }else if(value.exec){ // regex?
37503             f = function(n){
37504                 return value.test(n.attributes[attr]);
37505             };
37506         }else{
37507             throw 'Illegal filter type, must be string or regex';
37508         }
37509         this.filterBy(f, null, startNode);
37510         },
37511
37512     /**
37513      * Filter by a function. The passed function will be called with each
37514      * node in the tree (or from the startNode). If the function returns true, the node is kept
37515      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37516      * @param {Function} fn The filter function
37517      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37518      */
37519     filterBy : function(fn, scope, startNode){
37520         startNode = startNode || this.tree.root;
37521         if(this.autoClear){
37522             this.clear();
37523         }
37524         var af = this.filtered, rv = this.reverse;
37525         var f = function(n){
37526             if(n == startNode){
37527                 return true;
37528             }
37529             if(af[n.id]){
37530                 return false;
37531             }
37532             var m = fn.call(scope || n, n);
37533             if(!m || rv){
37534                 af[n.id] = n;
37535                 n.ui.hide();
37536                 return false;
37537             }
37538             return true;
37539         };
37540         startNode.cascade(f);
37541         if(this.remove){
37542            for(var id in af){
37543                if(typeof id != "function"){
37544                    var n = af[id];
37545                    if(n && n.parentNode){
37546                        n.parentNode.removeChild(n);
37547                    }
37548                }
37549            }
37550         }
37551     },
37552
37553     /**
37554      * Clears the current filter. Note: with the "remove" option
37555      * set a filter cannot be cleared.
37556      */
37557     clear : function(){
37558         var t = this.tree;
37559         var af = this.filtered;
37560         for(var id in af){
37561             if(typeof id != "function"){
37562                 var n = af[id];
37563                 if(n){
37564                     n.ui.show();
37565                 }
37566             }
37567         }
37568         this.filtered = {};
37569     }
37570 };
37571 /*
37572  * Based on:
37573  * Ext JS Library 1.1.1
37574  * Copyright(c) 2006-2007, Ext JS, LLC.
37575  *
37576  * Originally Released Under LGPL - original licence link has changed is not relivant.
37577  *
37578  * Fork - LGPL
37579  * <script type="text/javascript">
37580  */
37581  
37582
37583 /**
37584  * @class Roo.tree.TreeSorter
37585  * Provides sorting of nodes in a TreePanel
37586  * 
37587  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37588  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37589  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37590  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37591  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37592  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37593  * @constructor
37594  * @param {TreePanel} tree
37595  * @param {Object} config
37596  */
37597 Roo.tree.TreeSorter = function(tree, config){
37598     Roo.apply(this, config);
37599     tree.on("beforechildrenrendered", this.doSort, this);
37600     tree.on("append", this.updateSort, this);
37601     tree.on("insert", this.updateSort, this);
37602     
37603     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37604     var p = this.property || "text";
37605     var sortType = this.sortType;
37606     var fs = this.folderSort;
37607     var cs = this.caseSensitive === true;
37608     var leafAttr = this.leafAttr || 'leaf';
37609
37610     this.sortFn = function(n1, n2){
37611         if(fs){
37612             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37613                 return 1;
37614             }
37615             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37616                 return -1;
37617             }
37618         }
37619         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37620         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37621         if(v1 < v2){
37622                         return dsc ? +1 : -1;
37623                 }else if(v1 > v2){
37624                         return dsc ? -1 : +1;
37625         }else{
37626                 return 0;
37627         }
37628     };
37629 };
37630
37631 Roo.tree.TreeSorter.prototype = {
37632     doSort : function(node){
37633         node.sort(this.sortFn);
37634     },
37635     
37636     compareNodes : function(n1, n2){
37637         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37638     },
37639     
37640     updateSort : function(tree, node){
37641         if(node.childrenRendered){
37642             this.doSort.defer(1, this, [node]);
37643         }
37644     }
37645 };/*
37646  * Based on:
37647  * Ext JS Library 1.1.1
37648  * Copyright(c) 2006-2007, Ext JS, LLC.
37649  *
37650  * Originally Released Under LGPL - original licence link has changed is not relivant.
37651  *
37652  * Fork - LGPL
37653  * <script type="text/javascript">
37654  */
37655
37656 if(Roo.dd.DropZone){
37657     
37658 Roo.tree.TreeDropZone = function(tree, config){
37659     this.allowParentInsert = false;
37660     this.allowContainerDrop = false;
37661     this.appendOnly = false;
37662     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37663     this.tree = tree;
37664     this.lastInsertClass = "x-tree-no-status";
37665     this.dragOverData = {};
37666 };
37667
37668 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37669     ddGroup : "TreeDD",
37670     scroll:  true,
37671     
37672     expandDelay : 1000,
37673     
37674     expandNode : function(node){
37675         if(node.hasChildNodes() && !node.isExpanded()){
37676             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37677         }
37678     },
37679     
37680     queueExpand : function(node){
37681         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37682     },
37683     
37684     cancelExpand : function(){
37685         if(this.expandProcId){
37686             clearTimeout(this.expandProcId);
37687             this.expandProcId = false;
37688         }
37689     },
37690     
37691     isValidDropPoint : function(n, pt, dd, e, data){
37692         if(!n || !data){ return false; }
37693         var targetNode = n.node;
37694         var dropNode = data.node;
37695         // default drop rules
37696         if(!(targetNode && targetNode.isTarget && pt)){
37697             return false;
37698         }
37699         if(pt == "append" && targetNode.allowChildren === false){
37700             return false;
37701         }
37702         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37703             return false;
37704         }
37705         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37706             return false;
37707         }
37708         // reuse the object
37709         var overEvent = this.dragOverData;
37710         overEvent.tree = this.tree;
37711         overEvent.target = targetNode;
37712         overEvent.data = data;
37713         overEvent.point = pt;
37714         overEvent.source = dd;
37715         overEvent.rawEvent = e;
37716         overEvent.dropNode = dropNode;
37717         overEvent.cancel = false;  
37718         var result = this.tree.fireEvent("nodedragover", overEvent);
37719         return overEvent.cancel === false && result !== false;
37720     },
37721     
37722     getDropPoint : function(e, n, dd)
37723     {
37724         var tn = n.node;
37725         if(tn.isRoot){
37726             return tn.allowChildren !== false ? "append" : false; // always append for root
37727         }
37728         var dragEl = n.ddel;
37729         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37730         var y = Roo.lib.Event.getPageY(e);
37731         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37732         
37733         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37734         var noAppend = tn.allowChildren === false;
37735         if(this.appendOnly || tn.parentNode.allowChildren === false){
37736             return noAppend ? false : "append";
37737         }
37738         var noBelow = false;
37739         if(!this.allowParentInsert){
37740             noBelow = tn.hasChildNodes() && tn.isExpanded();
37741         }
37742         var q = (b - t) / (noAppend ? 2 : 3);
37743         if(y >= t && y < (t + q)){
37744             return "above";
37745         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37746             return "below";
37747         }else{
37748             return "append";
37749         }
37750     },
37751     
37752     onNodeEnter : function(n, dd, e, data)
37753     {
37754         this.cancelExpand();
37755     },
37756     
37757     onNodeOver : function(n, dd, e, data)
37758     {
37759        
37760         var pt = this.getDropPoint(e, n, dd);
37761         var node = n.node;
37762         
37763         // auto node expand check
37764         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37765             this.queueExpand(node);
37766         }else if(pt != "append"){
37767             this.cancelExpand();
37768         }
37769         
37770         // set the insert point style on the target node
37771         var returnCls = this.dropNotAllowed;
37772         if(this.isValidDropPoint(n, pt, dd, e, data)){
37773            if(pt){
37774                var el = n.ddel;
37775                var cls;
37776                if(pt == "above"){
37777                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37778                    cls = "x-tree-drag-insert-above";
37779                }else if(pt == "below"){
37780                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37781                    cls = "x-tree-drag-insert-below";
37782                }else{
37783                    returnCls = "x-tree-drop-ok-append";
37784                    cls = "x-tree-drag-append";
37785                }
37786                if(this.lastInsertClass != cls){
37787                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37788                    this.lastInsertClass = cls;
37789                }
37790            }
37791        }
37792        return returnCls;
37793     },
37794     
37795     onNodeOut : function(n, dd, e, data){
37796         
37797         this.cancelExpand();
37798         this.removeDropIndicators(n);
37799     },
37800     
37801     onNodeDrop : function(n, dd, e, data){
37802         var point = this.getDropPoint(e, n, dd);
37803         var targetNode = n.node;
37804         targetNode.ui.startDrop();
37805         if(!this.isValidDropPoint(n, point, dd, e, data)){
37806             targetNode.ui.endDrop();
37807             return false;
37808         }
37809         // first try to find the drop node
37810         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37811         var dropEvent = {
37812             tree : this.tree,
37813             target: targetNode,
37814             data: data,
37815             point: point,
37816             source: dd,
37817             rawEvent: e,
37818             dropNode: dropNode,
37819             cancel: !dropNode   
37820         };
37821         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37822         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37823             targetNode.ui.endDrop();
37824             return false;
37825         }
37826         // allow target changing
37827         targetNode = dropEvent.target;
37828         if(point == "append" && !targetNode.isExpanded()){
37829             targetNode.expand(false, null, function(){
37830                 this.completeDrop(dropEvent);
37831             }.createDelegate(this));
37832         }else{
37833             this.completeDrop(dropEvent);
37834         }
37835         return true;
37836     },
37837     
37838     completeDrop : function(de){
37839         var ns = de.dropNode, p = de.point, t = de.target;
37840         if(!(ns instanceof Array)){
37841             ns = [ns];
37842         }
37843         var n;
37844         for(var i = 0, len = ns.length; i < len; i++){
37845             n = ns[i];
37846             if(p == "above"){
37847                 t.parentNode.insertBefore(n, t);
37848             }else if(p == "below"){
37849                 t.parentNode.insertBefore(n, t.nextSibling);
37850             }else{
37851                 t.appendChild(n);
37852             }
37853         }
37854         n.ui.focus();
37855         if(this.tree.hlDrop){
37856             n.ui.highlight();
37857         }
37858         t.ui.endDrop();
37859         this.tree.fireEvent("nodedrop", de);
37860     },
37861     
37862     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37863         if(this.tree.hlDrop){
37864             dropNode.ui.focus();
37865             dropNode.ui.highlight();
37866         }
37867         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37868     },
37869     
37870     getTree : function(){
37871         return this.tree;
37872     },
37873     
37874     removeDropIndicators : function(n){
37875         if(n && n.ddel){
37876             var el = n.ddel;
37877             Roo.fly(el).removeClass([
37878                     "x-tree-drag-insert-above",
37879                     "x-tree-drag-insert-below",
37880                     "x-tree-drag-append"]);
37881             this.lastInsertClass = "_noclass";
37882         }
37883     },
37884     
37885     beforeDragDrop : function(target, e, id){
37886         this.cancelExpand();
37887         return true;
37888     },
37889     
37890     afterRepair : function(data){
37891         if(data && Roo.enableFx){
37892             data.node.ui.highlight();
37893         }
37894         this.hideProxy();
37895     } 
37896     
37897 });
37898
37899 }
37900 /*
37901  * Based on:
37902  * Ext JS Library 1.1.1
37903  * Copyright(c) 2006-2007, Ext JS, LLC.
37904  *
37905  * Originally Released Under LGPL - original licence link has changed is not relivant.
37906  *
37907  * Fork - LGPL
37908  * <script type="text/javascript">
37909  */
37910  
37911
37912 if(Roo.dd.DragZone){
37913 Roo.tree.TreeDragZone = function(tree, config){
37914     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37915     this.tree = tree;
37916 };
37917
37918 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37919     ddGroup : "TreeDD",
37920    
37921     onBeforeDrag : function(data, e){
37922         var n = data.node;
37923         return n && n.draggable && !n.disabled;
37924     },
37925      
37926     
37927     onInitDrag : function(e){
37928         var data = this.dragData;
37929         this.tree.getSelectionModel().select(data.node);
37930         this.proxy.update("");
37931         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37932         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37933     },
37934     
37935     getRepairXY : function(e, data){
37936         return data.node.ui.getDDRepairXY();
37937     },
37938     
37939     onEndDrag : function(data, e){
37940         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37941         
37942         
37943     },
37944     
37945     onValidDrop : function(dd, e, id){
37946         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37947         this.hideProxy();
37948     },
37949     
37950     beforeInvalidDrop : function(e, id){
37951         // this scrolls the original position back into view
37952         var sm = this.tree.getSelectionModel();
37953         sm.clearSelections();
37954         sm.select(this.dragData.node);
37955     }
37956 });
37957 }/*
37958  * Based on:
37959  * Ext JS Library 1.1.1
37960  * Copyright(c) 2006-2007, Ext JS, LLC.
37961  *
37962  * Originally Released Under LGPL - original licence link has changed is not relivant.
37963  *
37964  * Fork - LGPL
37965  * <script type="text/javascript">
37966  */
37967 /**
37968  * @class Roo.tree.TreeEditor
37969  * @extends Roo.Editor
37970  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37971  * as the editor field.
37972  * @constructor
37973  * @param {Object} config (used to be the tree panel.)
37974  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37975  * 
37976  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37977  * @cfg {Roo.form.TextField} field [required] The field configuration
37978  *
37979  * 
37980  */
37981 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37982     var tree = config;
37983     var field;
37984     if (oldconfig) { // old style..
37985         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37986     } else {
37987         // new style..
37988         tree = config.tree;
37989         config.field = config.field  || {};
37990         config.field.xtype = 'TextField';
37991         field = Roo.factory(config.field, Roo.form);
37992     }
37993     config = config || {};
37994     
37995     
37996     this.addEvents({
37997         /**
37998          * @event beforenodeedit
37999          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38000          * false from the handler of this event.
38001          * @param {Editor} this
38002          * @param {Roo.tree.Node} node 
38003          */
38004         "beforenodeedit" : true
38005     });
38006     
38007     //Roo.log(config);
38008     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38009
38010     this.tree = tree;
38011
38012     tree.on('beforeclick', this.beforeNodeClick, this);
38013     tree.getTreeEl().on('mousedown', this.hide, this);
38014     this.on('complete', this.updateNode, this);
38015     this.on('beforestartedit', this.fitToTree, this);
38016     this.on('startedit', this.bindScroll, this, {delay:10});
38017     this.on('specialkey', this.onSpecialKey, this);
38018 };
38019
38020 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38021     /**
38022      * @cfg {String} alignment
38023      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38024      */
38025     alignment: "l-l",
38026     // inherit
38027     autoSize: false,
38028     /**
38029      * @cfg {Boolean} hideEl
38030      * True to hide the bound element while the editor is displayed (defaults to false)
38031      */
38032     hideEl : false,
38033     /**
38034      * @cfg {String} cls
38035      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38036      */
38037     cls: "x-small-editor x-tree-editor",
38038     /**
38039      * @cfg {Boolean} shim
38040      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38041      */
38042     shim:false,
38043     // inherit
38044     shadow:"frame",
38045     /**
38046      * @cfg {Number} maxWidth
38047      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38048      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38049      * scroll and client offsets into account prior to each edit.
38050      */
38051     maxWidth: 250,
38052
38053     editDelay : 350,
38054
38055     // private
38056     fitToTree : function(ed, el){
38057         var td = this.tree.getTreeEl().dom, nd = el.dom;
38058         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38059             td.scrollLeft = nd.offsetLeft;
38060         }
38061         var w = Math.min(
38062                 this.maxWidth,
38063                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38064         this.setSize(w, '');
38065         
38066         return this.fireEvent('beforenodeedit', this, this.editNode);
38067         
38068     },
38069
38070     // private
38071     triggerEdit : function(node){
38072         this.completeEdit();
38073         this.editNode = node;
38074         this.startEdit(node.ui.textNode, node.text);
38075     },
38076
38077     // private
38078     bindScroll : function(){
38079         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38080     },
38081
38082     // private
38083     beforeNodeClick : function(node, e){
38084         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38085         this.lastClick = new Date();
38086         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38087             e.stopEvent();
38088             this.triggerEdit(node);
38089             return false;
38090         }
38091         return true;
38092     },
38093
38094     // private
38095     updateNode : function(ed, value){
38096         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38097         this.editNode.setText(value);
38098     },
38099
38100     // private
38101     onHide : function(){
38102         Roo.tree.TreeEditor.superclass.onHide.call(this);
38103         if(this.editNode){
38104             this.editNode.ui.focus();
38105         }
38106     },
38107
38108     // private
38109     onSpecialKey : function(field, e){
38110         var k = e.getKey();
38111         if(k == e.ESC){
38112             e.stopEvent();
38113             this.cancelEdit();
38114         }else if(k == e.ENTER && !e.hasModifier()){
38115             e.stopEvent();
38116             this.completeEdit();
38117         }
38118     }
38119 });//<Script type="text/javascript">
38120 /*
38121  * Based on:
38122  * Ext JS Library 1.1.1
38123  * Copyright(c) 2006-2007, Ext JS, LLC.
38124  *
38125  * Originally Released Under LGPL - original licence link has changed is not relivant.
38126  *
38127  * Fork - LGPL
38128  * <script type="text/javascript">
38129  */
38130  
38131 /**
38132  * Not documented??? - probably should be...
38133  */
38134
38135 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38136     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38137     
38138     renderElements : function(n, a, targetNode, bulkRender){
38139         //consel.log("renderElements?");
38140         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38141
38142         var t = n.getOwnerTree();
38143         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38144         
38145         var cols = t.columns;
38146         var bw = t.borderWidth;
38147         var c = cols[0];
38148         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38149          var cb = typeof a.checked == "boolean";
38150         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38151         var colcls = 'x-t-' + tid + '-c0';
38152         var buf = [
38153             '<li class="x-tree-node">',
38154             
38155                 
38156                 '<div class="x-tree-node-el ', a.cls,'">',
38157                     // extran...
38158                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38159                 
38160                 
38161                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38162                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38163                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38164                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38165                            (a.iconCls ? ' '+a.iconCls : ''),
38166                            '" unselectable="on" />',
38167                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38168                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38169                              
38170                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38171                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38172                             '<span unselectable="on" qtip="' + tx + '">',
38173                              tx,
38174                              '</span></a>' ,
38175                     '</div>',
38176                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38177                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38178                  ];
38179         for(var i = 1, len = cols.length; i < len; i++){
38180             c = cols[i];
38181             colcls = 'x-t-' + tid + '-c' +i;
38182             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38183             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38184                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38185                       "</div>");
38186          }
38187          
38188          buf.push(
38189             '</a>',
38190             '<div class="x-clear"></div></div>',
38191             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38192             "</li>");
38193         
38194         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38195             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38196                                 n.nextSibling.ui.getEl(), buf.join(""));
38197         }else{
38198             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38199         }
38200         var el = this.wrap.firstChild;
38201         this.elRow = el;
38202         this.elNode = el.firstChild;
38203         this.ranchor = el.childNodes[1];
38204         this.ctNode = this.wrap.childNodes[1];
38205         var cs = el.firstChild.childNodes;
38206         this.indentNode = cs[0];
38207         this.ecNode = cs[1];
38208         this.iconNode = cs[2];
38209         var index = 3;
38210         if(cb){
38211             this.checkbox = cs[3];
38212             index++;
38213         }
38214         this.anchor = cs[index];
38215         
38216         this.textNode = cs[index].firstChild;
38217         
38218         //el.on("click", this.onClick, this);
38219         //el.on("dblclick", this.onDblClick, this);
38220         
38221         
38222        // console.log(this);
38223     },
38224     initEvents : function(){
38225         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38226         
38227             
38228         var a = this.ranchor;
38229
38230         var el = Roo.get(a);
38231
38232         if(Roo.isOpera){ // opera render bug ignores the CSS
38233             el.setStyle("text-decoration", "none");
38234         }
38235
38236         el.on("click", this.onClick, this);
38237         el.on("dblclick", this.onDblClick, this);
38238         el.on("contextmenu", this.onContextMenu, this);
38239         
38240     },
38241     
38242     /*onSelectedChange : function(state){
38243         if(state){
38244             this.focus();
38245             this.addClass("x-tree-selected");
38246         }else{
38247             //this.blur();
38248             this.removeClass("x-tree-selected");
38249         }
38250     },*/
38251     addClass : function(cls){
38252         if(this.elRow){
38253             Roo.fly(this.elRow).addClass(cls);
38254         }
38255         
38256     },
38257     
38258     
38259     removeClass : function(cls){
38260         if(this.elRow){
38261             Roo.fly(this.elRow).removeClass(cls);
38262         }
38263     }
38264
38265     
38266     
38267 });//<Script type="text/javascript">
38268
38269 /*
38270  * Based on:
38271  * Ext JS Library 1.1.1
38272  * Copyright(c) 2006-2007, Ext JS, LLC.
38273  *
38274  * Originally Released Under LGPL - original licence link has changed is not relivant.
38275  *
38276  * Fork - LGPL
38277  * <script type="text/javascript">
38278  */
38279  
38280
38281 /**
38282  * @class Roo.tree.ColumnTree
38283  * @extends Roo.tree.TreePanel
38284  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38285  * @cfg {int} borderWidth  compined right/left border allowance
38286  * @constructor
38287  * @param {String/HTMLElement/Element} el The container element
38288  * @param {Object} config
38289  */
38290 Roo.tree.ColumnTree =  function(el, config)
38291 {
38292    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38293    this.addEvents({
38294         /**
38295         * @event resize
38296         * Fire this event on a container when it resizes
38297         * @param {int} w Width
38298         * @param {int} h Height
38299         */
38300        "resize" : true
38301     });
38302     this.on('resize', this.onResize, this);
38303 };
38304
38305 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38306     //lines:false,
38307     
38308     
38309     borderWidth: Roo.isBorderBox ? 0 : 2, 
38310     headEls : false,
38311     
38312     render : function(){
38313         // add the header.....
38314        
38315         Roo.tree.ColumnTree.superclass.render.apply(this);
38316         
38317         this.el.addClass('x-column-tree');
38318         
38319         this.headers = this.el.createChild(
38320             {cls:'x-tree-headers'},this.innerCt.dom);
38321    
38322         var cols = this.columns, c;
38323         var totalWidth = 0;
38324         this.headEls = [];
38325         var  len = cols.length;
38326         for(var i = 0; i < len; i++){
38327              c = cols[i];
38328              totalWidth += c.width;
38329             this.headEls.push(this.headers.createChild({
38330                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38331                  cn: {
38332                      cls:'x-tree-hd-text',
38333                      html: c.header
38334                  },
38335                  style:'width:'+(c.width-this.borderWidth)+'px;'
38336              }));
38337         }
38338         this.headers.createChild({cls:'x-clear'});
38339         // prevent floats from wrapping when clipped
38340         this.headers.setWidth(totalWidth);
38341         //this.innerCt.setWidth(totalWidth);
38342         this.innerCt.setStyle({ overflow: 'auto' });
38343         this.onResize(this.width, this.height);
38344              
38345         
38346     },
38347     onResize : function(w,h)
38348     {
38349         this.height = h;
38350         this.width = w;
38351         // resize cols..
38352         this.innerCt.setWidth(this.width);
38353         this.innerCt.setHeight(this.height-20);
38354         
38355         // headers...
38356         var cols = this.columns, c;
38357         var totalWidth = 0;
38358         var expEl = false;
38359         var len = cols.length;
38360         for(var i = 0; i < len; i++){
38361             c = cols[i];
38362             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38363                 // it's the expander..
38364                 expEl  = this.headEls[i];
38365                 continue;
38366             }
38367             totalWidth += c.width;
38368             
38369         }
38370         if (expEl) {
38371             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38372         }
38373         this.headers.setWidth(w-20);
38374
38375         
38376         
38377         
38378     }
38379 });
38380 /*
38381  * Based on:
38382  * Ext JS Library 1.1.1
38383  * Copyright(c) 2006-2007, Ext JS, LLC.
38384  *
38385  * Originally Released Under LGPL - original licence link has changed is not relivant.
38386  *
38387  * Fork - LGPL
38388  * <script type="text/javascript">
38389  */
38390  
38391 /**
38392  * @class Roo.menu.Menu
38393  * @extends Roo.util.Observable
38394  * @children Roo.menu.BaseItem
38395  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38396  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38397  * @constructor
38398  * Creates a new Menu
38399  * @param {Object} config Configuration options
38400  */
38401 Roo.menu.Menu = function(config){
38402     
38403     Roo.menu.Menu.superclass.constructor.call(this, config);
38404     
38405     this.id = this.id || Roo.id();
38406     this.addEvents({
38407         /**
38408          * @event beforeshow
38409          * Fires before this menu is displayed
38410          * @param {Roo.menu.Menu} this
38411          */
38412         beforeshow : true,
38413         /**
38414          * @event beforehide
38415          * Fires before this menu is hidden
38416          * @param {Roo.menu.Menu} this
38417          */
38418         beforehide : true,
38419         /**
38420          * @event show
38421          * Fires after this menu is displayed
38422          * @param {Roo.menu.Menu} this
38423          */
38424         show : true,
38425         /**
38426          * @event hide
38427          * Fires after this menu is hidden
38428          * @param {Roo.menu.Menu} this
38429          */
38430         hide : true,
38431         /**
38432          * @event click
38433          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38434          * @param {Roo.menu.Menu} this
38435          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38436          * @param {Roo.EventObject} e
38437          */
38438         click : true,
38439         /**
38440          * @event mouseover
38441          * Fires when the mouse is hovering over this menu
38442          * @param {Roo.menu.Menu} this
38443          * @param {Roo.EventObject} e
38444          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38445          */
38446         mouseover : true,
38447         /**
38448          * @event mouseout
38449          * Fires when the mouse exits this menu
38450          * @param {Roo.menu.Menu} this
38451          * @param {Roo.EventObject} e
38452          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38453          */
38454         mouseout : true,
38455         /**
38456          * @event itemclick
38457          * Fires when a menu item contained in this menu is clicked
38458          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38459          * @param {Roo.EventObject} e
38460          */
38461         itemclick: true
38462     });
38463     if (this.registerMenu) {
38464         Roo.menu.MenuMgr.register(this);
38465     }
38466     
38467     var mis = this.items;
38468     this.items = new Roo.util.MixedCollection();
38469     if(mis){
38470         this.add.apply(this, mis);
38471     }
38472 };
38473
38474 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38475     /**
38476      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38477      */
38478     minWidth : 120,
38479     /**
38480      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38481      * for bottom-right shadow (defaults to "sides")
38482      */
38483     shadow : "sides",
38484     /**
38485      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38486      * this menu (defaults to "tl-tr?")
38487      */
38488     subMenuAlign : "tl-tr?",
38489     /**
38490      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38491      * relative to its element of origin (defaults to "tl-bl?")
38492      */
38493     defaultAlign : "tl-bl?",
38494     /**
38495      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38496      */
38497     allowOtherMenus : false,
38498     /**
38499      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38500      */
38501     registerMenu : true,
38502
38503     hidden:true,
38504
38505     // private
38506     render : function(){
38507         if(this.el){
38508             return;
38509         }
38510         var el = this.el = new Roo.Layer({
38511             cls: "x-menu",
38512             shadow:this.shadow,
38513             constrain: false,
38514             parentEl: this.parentEl || document.body,
38515             zindex:15000
38516         });
38517
38518         this.keyNav = new Roo.menu.MenuNav(this);
38519
38520         if(this.plain){
38521             el.addClass("x-menu-plain");
38522         }
38523         if(this.cls){
38524             el.addClass(this.cls);
38525         }
38526         // generic focus element
38527         this.focusEl = el.createChild({
38528             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38529         });
38530         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38531         //disabling touch- as it's causing issues ..
38532         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38533         ul.on('click'   , this.onClick, this);
38534         
38535         
38536         ul.on("mouseover", this.onMouseOver, this);
38537         ul.on("mouseout", this.onMouseOut, this);
38538         this.items.each(function(item){
38539             if (item.hidden) {
38540                 return;
38541             }
38542             
38543             var li = document.createElement("li");
38544             li.className = "x-menu-list-item";
38545             ul.dom.appendChild(li);
38546             item.render(li, this);
38547         }, this);
38548         this.ul = ul;
38549         this.autoWidth();
38550     },
38551
38552     // private
38553     autoWidth : function(){
38554         var el = this.el, ul = this.ul;
38555         if(!el){
38556             return;
38557         }
38558         var w = this.width;
38559         if(w){
38560             el.setWidth(w);
38561         }else if(Roo.isIE){
38562             el.setWidth(this.minWidth);
38563             var t = el.dom.offsetWidth; // force recalc
38564             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38565         }
38566     },
38567
38568     // private
38569     delayAutoWidth : function(){
38570         if(this.rendered){
38571             if(!this.awTask){
38572                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38573             }
38574             this.awTask.delay(20);
38575         }
38576     },
38577
38578     // private
38579     findTargetItem : function(e){
38580         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38581         if(t && t.menuItemId){
38582             return this.items.get(t.menuItemId);
38583         }
38584     },
38585
38586     // private
38587     onClick : function(e){
38588         Roo.log("menu.onClick");
38589         var t = this.findTargetItem(e);
38590         if(!t){
38591             return;
38592         }
38593         Roo.log(e);
38594         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38595             if(t == this.activeItem && t.shouldDeactivate(e)){
38596                 this.activeItem.deactivate();
38597                 delete this.activeItem;
38598                 return;
38599             }
38600             if(t.canActivate){
38601                 this.setActiveItem(t, true);
38602             }
38603             return;
38604             
38605             
38606         }
38607         
38608         t.onClick(e);
38609         this.fireEvent("click", this, t, e);
38610     },
38611
38612     // private
38613     setActiveItem : function(item, autoExpand){
38614         if(item != this.activeItem){
38615             if(this.activeItem){
38616                 this.activeItem.deactivate();
38617             }
38618             this.activeItem = item;
38619             item.activate(autoExpand);
38620         }else if(autoExpand){
38621             item.expandMenu();
38622         }
38623     },
38624
38625     // private
38626     tryActivate : function(start, step){
38627         var items = this.items;
38628         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38629             var item = items.get(i);
38630             if(!item.disabled && item.canActivate){
38631                 this.setActiveItem(item, false);
38632                 return item;
38633             }
38634         }
38635         return false;
38636     },
38637
38638     // private
38639     onMouseOver : function(e){
38640         var t;
38641         if(t = this.findTargetItem(e)){
38642             if(t.canActivate && !t.disabled){
38643                 this.setActiveItem(t, true);
38644             }
38645         }
38646         this.fireEvent("mouseover", this, e, t);
38647     },
38648
38649     // private
38650     onMouseOut : function(e){
38651         var t;
38652         if(t = this.findTargetItem(e)){
38653             if(t == this.activeItem && t.shouldDeactivate(e)){
38654                 this.activeItem.deactivate();
38655                 delete this.activeItem;
38656             }
38657         }
38658         this.fireEvent("mouseout", this, e, t);
38659     },
38660
38661     /**
38662      * Read-only.  Returns true if the menu is currently displayed, else false.
38663      * @type Boolean
38664      */
38665     isVisible : function(){
38666         return this.el && !this.hidden;
38667     },
38668
38669     /**
38670      * Displays this menu relative to another element
38671      * @param {String/HTMLElement/Roo.Element} element The element to align to
38672      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38673      * the element (defaults to this.defaultAlign)
38674      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38675      */
38676     show : function(el, pos, parentMenu){
38677         this.parentMenu = parentMenu;
38678         if(!this.el){
38679             this.render();
38680         }
38681         this.fireEvent("beforeshow", this);
38682         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38683     },
38684
38685     /**
38686      * Displays this menu at a specific xy position
38687      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38688      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38689      */
38690     showAt : function(xy, parentMenu, /* private: */_e){
38691         this.parentMenu = parentMenu;
38692         if(!this.el){
38693             this.render();
38694         }
38695         if(_e !== false){
38696             this.fireEvent("beforeshow", this);
38697             xy = this.el.adjustForConstraints(xy);
38698         }
38699         this.el.setXY(xy);
38700         this.el.show();
38701         this.hidden = false;
38702         this.focus();
38703         this.fireEvent("show", this);
38704     },
38705
38706     focus : function(){
38707         if(!this.hidden){
38708             this.doFocus.defer(50, this);
38709         }
38710     },
38711
38712     doFocus : function(){
38713         if(!this.hidden){
38714             this.focusEl.focus();
38715         }
38716     },
38717
38718     /**
38719      * Hides this menu and optionally all parent menus
38720      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38721      */
38722     hide : function(deep){
38723         if(this.el && this.isVisible()){
38724             this.fireEvent("beforehide", this);
38725             if(this.activeItem){
38726                 this.activeItem.deactivate();
38727                 this.activeItem = null;
38728             }
38729             this.el.hide();
38730             this.hidden = true;
38731             this.fireEvent("hide", this);
38732         }
38733         if(deep === true && this.parentMenu){
38734             this.parentMenu.hide(true);
38735         }
38736     },
38737
38738     /**
38739      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38740      * Any of the following are valid:
38741      * <ul>
38742      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38743      * <li>An HTMLElement object which will be converted to a menu item</li>
38744      * <li>A menu item config object that will be created as a new menu item</li>
38745      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38746      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38747      * </ul>
38748      * Usage:
38749      * <pre><code>
38750 // Create the menu
38751 var menu = new Roo.menu.Menu();
38752
38753 // Create a menu item to add by reference
38754 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38755
38756 // Add a bunch of items at once using different methods.
38757 // Only the last item added will be returned.
38758 var item = menu.add(
38759     menuItem,                // add existing item by ref
38760     'Dynamic Item',          // new TextItem
38761     '-',                     // new separator
38762     { text: 'Config Item' }  // new item by config
38763 );
38764 </code></pre>
38765      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38766      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38767      */
38768     add : function(){
38769         var a = arguments, l = a.length, item;
38770         for(var i = 0; i < l; i++){
38771             var el = a[i];
38772             if ((typeof(el) == "object") && el.xtype && el.xns) {
38773                 el = Roo.factory(el, Roo.menu);
38774             }
38775             
38776             if(el.render){ // some kind of Item
38777                 item = this.addItem(el);
38778             }else if(typeof el == "string"){ // string
38779                 if(el == "separator" || el == "-"){
38780                     item = this.addSeparator();
38781                 }else{
38782                     item = this.addText(el);
38783                 }
38784             }else if(el.tagName || el.el){ // element
38785                 item = this.addElement(el);
38786             }else if(typeof el == "object"){ // must be menu item config?
38787                 item = this.addMenuItem(el);
38788             }
38789         }
38790         return item;
38791     },
38792
38793     /**
38794      * Returns this menu's underlying {@link Roo.Element} object
38795      * @return {Roo.Element} The element
38796      */
38797     getEl : function(){
38798         if(!this.el){
38799             this.render();
38800         }
38801         return this.el;
38802     },
38803
38804     /**
38805      * Adds a separator bar to the menu
38806      * @return {Roo.menu.Item} The menu item that was added
38807      */
38808     addSeparator : function(){
38809         return this.addItem(new Roo.menu.Separator());
38810     },
38811
38812     /**
38813      * Adds an {@link Roo.Element} object to the menu
38814      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38815      * @return {Roo.menu.Item} The menu item that was added
38816      */
38817     addElement : function(el){
38818         return this.addItem(new Roo.menu.BaseItem(el));
38819     },
38820
38821     /**
38822      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38823      * @param {Roo.menu.Item} item The menu item to add
38824      * @return {Roo.menu.Item} The menu item that was added
38825      */
38826     addItem : function(item){
38827         this.items.add(item);
38828         if(this.ul){
38829             var li = document.createElement("li");
38830             li.className = "x-menu-list-item";
38831             this.ul.dom.appendChild(li);
38832             item.render(li, this);
38833             this.delayAutoWidth();
38834         }
38835         return item;
38836     },
38837
38838     /**
38839      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38840      * @param {Object} config A MenuItem config object
38841      * @return {Roo.menu.Item} The menu item that was added
38842      */
38843     addMenuItem : function(config){
38844         if(!(config instanceof Roo.menu.Item)){
38845             if(typeof config.checked == "boolean"){ // must be check menu item config?
38846                 config = new Roo.menu.CheckItem(config);
38847             }else{
38848                 config = new Roo.menu.Item(config);
38849             }
38850         }
38851         return this.addItem(config);
38852     },
38853
38854     /**
38855      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38856      * @param {String} text The text to display in the menu item
38857      * @return {Roo.menu.Item} The menu item that was added
38858      */
38859     addText : function(text){
38860         return this.addItem(new Roo.menu.TextItem({ text : text }));
38861     },
38862
38863     /**
38864      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38865      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38866      * @param {Roo.menu.Item} item The menu item to add
38867      * @return {Roo.menu.Item} The menu item that was added
38868      */
38869     insert : function(index, item){
38870         this.items.insert(index, item);
38871         if(this.ul){
38872             var li = document.createElement("li");
38873             li.className = "x-menu-list-item";
38874             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38875             item.render(li, this);
38876             this.delayAutoWidth();
38877         }
38878         return item;
38879     },
38880
38881     /**
38882      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38883      * @param {Roo.menu.Item} item The menu item to remove
38884      */
38885     remove : function(item){
38886         this.items.removeKey(item.id);
38887         item.destroy();
38888     },
38889
38890     /**
38891      * Removes and destroys all items in the menu
38892      */
38893     removeAll : function(){
38894         var f;
38895         while(f = this.items.first()){
38896             this.remove(f);
38897         }
38898     }
38899 });
38900
38901 // MenuNav is a private utility class used internally by the Menu
38902 Roo.menu.MenuNav = function(menu){
38903     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38904     this.scope = this.menu = menu;
38905 };
38906
38907 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38908     doRelay : function(e, h){
38909         var k = e.getKey();
38910         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38911             this.menu.tryActivate(0, 1);
38912             return false;
38913         }
38914         return h.call(this.scope || this, e, this.menu);
38915     },
38916
38917     up : function(e, m){
38918         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38919             m.tryActivate(m.items.length-1, -1);
38920         }
38921     },
38922
38923     down : function(e, m){
38924         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38925             m.tryActivate(0, 1);
38926         }
38927     },
38928
38929     right : function(e, m){
38930         if(m.activeItem){
38931             m.activeItem.expandMenu(true);
38932         }
38933     },
38934
38935     left : function(e, m){
38936         m.hide();
38937         if(m.parentMenu && m.parentMenu.activeItem){
38938             m.parentMenu.activeItem.activate();
38939         }
38940     },
38941
38942     enter : function(e, m){
38943         if(m.activeItem){
38944             e.stopPropagation();
38945             m.activeItem.onClick(e);
38946             m.fireEvent("click", this, m.activeItem);
38947             return true;
38948         }
38949     }
38950 });/*
38951  * Based on:
38952  * Ext JS Library 1.1.1
38953  * Copyright(c) 2006-2007, Ext JS, LLC.
38954  *
38955  * Originally Released Under LGPL - original licence link has changed is not relivant.
38956  *
38957  * Fork - LGPL
38958  * <script type="text/javascript">
38959  */
38960  
38961 /**
38962  * @class Roo.menu.MenuMgr
38963  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38964  * @static
38965  */
38966 Roo.menu.MenuMgr = function(){
38967    var menus, active, groups = {}, attached = false, lastShow = new Date();
38968
38969    // private - called when first menu is created
38970    function init(){
38971        menus = {};
38972        active = new Roo.util.MixedCollection();
38973        Roo.get(document).addKeyListener(27, function(){
38974            if(active.length > 0){
38975                hideAll();
38976            }
38977        });
38978    }
38979
38980    // private
38981    function hideAll(){
38982        if(active && active.length > 0){
38983            var c = active.clone();
38984            c.each(function(m){
38985                m.hide();
38986            });
38987        }
38988    }
38989
38990    // private
38991    function onHide(m){
38992        active.remove(m);
38993        if(active.length < 1){
38994            Roo.get(document).un("mousedown", onMouseDown);
38995            attached = false;
38996        }
38997    }
38998
38999    // private
39000    function onShow(m){
39001        var last = active.last();
39002        lastShow = new Date();
39003        active.add(m);
39004        if(!attached){
39005            Roo.get(document).on("mousedown", onMouseDown);
39006            attached = true;
39007        }
39008        if(m.parentMenu){
39009           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39010           m.parentMenu.activeChild = m;
39011        }else if(last && last.isVisible()){
39012           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39013        }
39014    }
39015
39016    // private
39017    function onBeforeHide(m){
39018        if(m.activeChild){
39019            m.activeChild.hide();
39020        }
39021        if(m.autoHideTimer){
39022            clearTimeout(m.autoHideTimer);
39023            delete m.autoHideTimer;
39024        }
39025    }
39026
39027    // private
39028    function onBeforeShow(m){
39029        var pm = m.parentMenu;
39030        if(!pm && !m.allowOtherMenus){
39031            hideAll();
39032        }else if(pm && pm.activeChild && active != m){
39033            pm.activeChild.hide();
39034        }
39035    }
39036
39037    // private
39038    function onMouseDown(e){
39039        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39040            hideAll();
39041        }
39042    }
39043
39044    // private
39045    function onBeforeCheck(mi, state){
39046        if(state){
39047            var g = groups[mi.group];
39048            for(var i = 0, l = g.length; i < l; i++){
39049                if(g[i] != mi){
39050                    g[i].setChecked(false);
39051                }
39052            }
39053        }
39054    }
39055
39056    return {
39057
39058        /**
39059         * Hides all menus that are currently visible
39060         */
39061        hideAll : function(){
39062             hideAll();  
39063        },
39064
39065        // private
39066        register : function(menu){
39067            if(!menus){
39068                init();
39069            }
39070            menus[menu.id] = menu;
39071            menu.on("beforehide", onBeforeHide);
39072            menu.on("hide", onHide);
39073            menu.on("beforeshow", onBeforeShow);
39074            menu.on("show", onShow);
39075            var g = menu.group;
39076            if(g && menu.events["checkchange"]){
39077                if(!groups[g]){
39078                    groups[g] = [];
39079                }
39080                groups[g].push(menu);
39081                menu.on("checkchange", onCheck);
39082            }
39083        },
39084
39085         /**
39086          * Returns a {@link Roo.menu.Menu} object
39087          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39088          * be used to generate and return a new Menu instance.
39089          */
39090        get : function(menu){
39091            if(typeof menu == "string"){ // menu id
39092                return menus[menu];
39093            }else if(menu.events){  // menu instance
39094                return menu;
39095            }else if(typeof menu.length == 'number'){ // array of menu items?
39096                return new Roo.menu.Menu({items:menu});
39097            }else{ // otherwise, must be a config
39098                return new Roo.menu.Menu(menu);
39099            }
39100        },
39101
39102        // private
39103        unregister : function(menu){
39104            delete menus[menu.id];
39105            menu.un("beforehide", onBeforeHide);
39106            menu.un("hide", onHide);
39107            menu.un("beforeshow", onBeforeShow);
39108            menu.un("show", onShow);
39109            var g = menu.group;
39110            if(g && menu.events["checkchange"]){
39111                groups[g].remove(menu);
39112                menu.un("checkchange", onCheck);
39113            }
39114        },
39115
39116        // private
39117        registerCheckable : function(menuItem){
39118            var g = menuItem.group;
39119            if(g){
39120                if(!groups[g]){
39121                    groups[g] = [];
39122                }
39123                groups[g].push(menuItem);
39124                menuItem.on("beforecheckchange", onBeforeCheck);
39125            }
39126        },
39127
39128        // private
39129        unregisterCheckable : function(menuItem){
39130            var g = menuItem.group;
39131            if(g){
39132                groups[g].remove(menuItem);
39133                menuItem.un("beforecheckchange", onBeforeCheck);
39134            }
39135        }
39136    };
39137 }();/*
39138  * Based on:
39139  * Ext JS Library 1.1.1
39140  * Copyright(c) 2006-2007, Ext JS, LLC.
39141  *
39142  * Originally Released Under LGPL - original licence link has changed is not relivant.
39143  *
39144  * Fork - LGPL
39145  * <script type="text/javascript">
39146  */
39147  
39148
39149 /**
39150  * @class Roo.menu.BaseItem
39151  * @extends Roo.Component
39152  * @abstract
39153  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39154  * management and base configuration options shared by all menu components.
39155  * @constructor
39156  * Creates a new BaseItem
39157  * @param {Object} config Configuration options
39158  */
39159 Roo.menu.BaseItem = function(config){
39160     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39161
39162     this.addEvents({
39163         /**
39164          * @event click
39165          * Fires when this item is clicked
39166          * @param {Roo.menu.BaseItem} this
39167          * @param {Roo.EventObject} e
39168          */
39169         click: true,
39170         /**
39171          * @event activate
39172          * Fires when this item is activated
39173          * @param {Roo.menu.BaseItem} this
39174          */
39175         activate : true,
39176         /**
39177          * @event deactivate
39178          * Fires when this item is deactivated
39179          * @param {Roo.menu.BaseItem} this
39180          */
39181         deactivate : true
39182     });
39183
39184     if(this.handler){
39185         this.on("click", this.handler, this.scope, true);
39186     }
39187 };
39188
39189 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39190     /**
39191      * @cfg {Function} handler
39192      * A function that will handle the click event of this menu item (defaults to undefined)
39193      */
39194     /**
39195      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39196      */
39197     canActivate : false,
39198     
39199      /**
39200      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39201      */
39202     hidden: false,
39203     
39204     /**
39205      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39206      */
39207     activeClass : "x-menu-item-active",
39208     /**
39209      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39210      */
39211     hideOnClick : true,
39212     /**
39213      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39214      */
39215     hideDelay : 100,
39216
39217     // private
39218     ctype: "Roo.menu.BaseItem",
39219
39220     // private
39221     actionMode : "container",
39222
39223     // private
39224     render : function(container, parentMenu){
39225         this.parentMenu = parentMenu;
39226         Roo.menu.BaseItem.superclass.render.call(this, container);
39227         this.container.menuItemId = this.id;
39228     },
39229
39230     // private
39231     onRender : function(container, position){
39232         this.el = Roo.get(this.el);
39233         container.dom.appendChild(this.el.dom);
39234     },
39235
39236     // private
39237     onClick : function(e){
39238         if(!this.disabled && this.fireEvent("click", this, e) !== false
39239                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39240             this.handleClick(e);
39241         }else{
39242             e.stopEvent();
39243         }
39244     },
39245
39246     // private
39247     activate : function(){
39248         if(this.disabled){
39249             return false;
39250         }
39251         var li = this.container;
39252         li.addClass(this.activeClass);
39253         this.region = li.getRegion().adjust(2, 2, -2, -2);
39254         this.fireEvent("activate", this);
39255         return true;
39256     },
39257
39258     // private
39259     deactivate : function(){
39260         this.container.removeClass(this.activeClass);
39261         this.fireEvent("deactivate", this);
39262     },
39263
39264     // private
39265     shouldDeactivate : function(e){
39266         return !this.region || !this.region.contains(e.getPoint());
39267     },
39268
39269     // private
39270     handleClick : function(e){
39271         if(this.hideOnClick){
39272             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39273         }
39274     },
39275
39276     // private
39277     expandMenu : function(autoActivate){
39278         // do nothing
39279     },
39280
39281     // private
39282     hideMenu : function(){
39283         // do nothing
39284     }
39285 });/*
39286  * Based on:
39287  * Ext JS Library 1.1.1
39288  * Copyright(c) 2006-2007, Ext JS, LLC.
39289  *
39290  * Originally Released Under LGPL - original licence link has changed is not relivant.
39291  *
39292  * Fork - LGPL
39293  * <script type="text/javascript">
39294  */
39295  
39296 /**
39297  * @class Roo.menu.Adapter
39298  * @extends Roo.menu.BaseItem
39299  * @abstract
39300  * 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.
39301  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39302  * @constructor
39303  * Creates a new Adapter
39304  * @param {Object} config Configuration options
39305  */
39306 Roo.menu.Adapter = function(component, config){
39307     Roo.menu.Adapter.superclass.constructor.call(this, config);
39308     this.component = component;
39309 };
39310 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39311     // private
39312     canActivate : true,
39313
39314     // private
39315     onRender : function(container, position){
39316         this.component.render(container);
39317         this.el = this.component.getEl();
39318     },
39319
39320     // private
39321     activate : function(){
39322         if(this.disabled){
39323             return false;
39324         }
39325         this.component.focus();
39326         this.fireEvent("activate", this);
39327         return true;
39328     },
39329
39330     // private
39331     deactivate : function(){
39332         this.fireEvent("deactivate", this);
39333     },
39334
39335     // private
39336     disable : function(){
39337         this.component.disable();
39338         Roo.menu.Adapter.superclass.disable.call(this);
39339     },
39340
39341     // private
39342     enable : function(){
39343         this.component.enable();
39344         Roo.menu.Adapter.superclass.enable.call(this);
39345     }
39346 });/*
39347  * Based on:
39348  * Ext JS Library 1.1.1
39349  * Copyright(c) 2006-2007, Ext JS, LLC.
39350  *
39351  * Originally Released Under LGPL - original licence link has changed is not relivant.
39352  *
39353  * Fork - LGPL
39354  * <script type="text/javascript">
39355  */
39356
39357 /**
39358  * @class Roo.menu.TextItem
39359  * @extends Roo.menu.BaseItem
39360  * Adds a static text string to a menu, usually used as either a heading or group separator.
39361  * Note: old style constructor with text is still supported.
39362  * 
39363  * @constructor
39364  * Creates a new TextItem
39365  * @param {Object} cfg Configuration
39366  */
39367 Roo.menu.TextItem = function(cfg){
39368     if (typeof(cfg) == 'string') {
39369         this.text = cfg;
39370     } else {
39371         Roo.apply(this,cfg);
39372     }
39373     
39374     Roo.menu.TextItem.superclass.constructor.call(this);
39375 };
39376
39377 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39378     /**
39379      * @cfg {String} text Text to show on item.
39380      */
39381     text : '',
39382     
39383     /**
39384      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39385      */
39386     hideOnClick : false,
39387     /**
39388      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39389      */
39390     itemCls : "x-menu-text",
39391
39392     // private
39393     onRender : function(){
39394         var s = document.createElement("span");
39395         s.className = this.itemCls;
39396         s.innerHTML = this.text;
39397         this.el = s;
39398         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39399     }
39400 });/*
39401  * Based on:
39402  * Ext JS Library 1.1.1
39403  * Copyright(c) 2006-2007, Ext JS, LLC.
39404  *
39405  * Originally Released Under LGPL - original licence link has changed is not relivant.
39406  *
39407  * Fork - LGPL
39408  * <script type="text/javascript">
39409  */
39410
39411 /**
39412  * @class Roo.menu.Separator
39413  * @extends Roo.menu.BaseItem
39414  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39415  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39416  * @constructor
39417  * @param {Object} config Configuration options
39418  */
39419 Roo.menu.Separator = function(config){
39420     Roo.menu.Separator.superclass.constructor.call(this, config);
39421 };
39422
39423 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39424     /**
39425      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39426      */
39427     itemCls : "x-menu-sep",
39428     /**
39429      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39430      */
39431     hideOnClick : false,
39432
39433     // private
39434     onRender : function(li){
39435         var s = document.createElement("span");
39436         s.className = this.itemCls;
39437         s.innerHTML = "&#160;";
39438         this.el = s;
39439         li.addClass("x-menu-sep-li");
39440         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39441     }
39442 });/*
39443  * Based on:
39444  * Ext JS Library 1.1.1
39445  * Copyright(c) 2006-2007, Ext JS, LLC.
39446  *
39447  * Originally Released Under LGPL - original licence link has changed is not relivant.
39448  *
39449  * Fork - LGPL
39450  * <script type="text/javascript">
39451  */
39452 /**
39453  * @class Roo.menu.Item
39454  * @extends Roo.menu.BaseItem
39455  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39456  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39457  * activation and click handling.
39458  * @constructor
39459  * Creates a new Item
39460  * @param {Object} config Configuration options
39461  */
39462 Roo.menu.Item = function(config){
39463     Roo.menu.Item.superclass.constructor.call(this, config);
39464     if(this.menu){
39465         this.menu = Roo.menu.MenuMgr.get(this.menu);
39466     }
39467 };
39468 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39469     /**
39470      * @cfg {Roo.menu.Menu} menu
39471      * A Sub menu
39472      */
39473     /**
39474      * @cfg {String} text
39475      * The text to show on the menu item.
39476      */
39477     text: '',
39478      /**
39479      * @cfg {String} HTML to render in menu
39480      * The text to show on the menu item (HTML version).
39481      */
39482     html: '',
39483     /**
39484      * @cfg {String} icon
39485      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39486      */
39487     icon: undefined,
39488     /**
39489      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39490      */
39491     itemCls : "x-menu-item",
39492     /**
39493      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39494      */
39495     canActivate : true,
39496     /**
39497      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39498      */
39499     showDelay: 200,
39500     // doc'd in BaseItem
39501     hideDelay: 200,
39502
39503     // private
39504     ctype: "Roo.menu.Item",
39505     
39506     // private
39507     onRender : function(container, position){
39508         var el = document.createElement("a");
39509         el.hideFocus = true;
39510         el.unselectable = "on";
39511         el.href = this.href || "#";
39512         if(this.hrefTarget){
39513             el.target = this.hrefTarget;
39514         }
39515         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39516         
39517         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39518         
39519         el.innerHTML = String.format(
39520                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39521                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39522         this.el = el;
39523         Roo.menu.Item.superclass.onRender.call(this, container, position);
39524     },
39525
39526     /**
39527      * Sets the text to display in this menu item
39528      * @param {String} text The text to display
39529      * @param {Boolean} isHTML true to indicate text is pure html.
39530      */
39531     setText : function(text, isHTML){
39532         if (isHTML) {
39533             this.html = text;
39534         } else {
39535             this.text = text;
39536             this.html = '';
39537         }
39538         if(this.rendered){
39539             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39540      
39541             this.el.update(String.format(
39542                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39543                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39544             this.parentMenu.autoWidth();
39545         }
39546     },
39547
39548     // private
39549     handleClick : function(e){
39550         if(!this.href){ // if no link defined, stop the event automatically
39551             e.stopEvent();
39552         }
39553         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39554     },
39555
39556     // private
39557     activate : function(autoExpand){
39558         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39559             this.focus();
39560             if(autoExpand){
39561                 this.expandMenu();
39562             }
39563         }
39564         return true;
39565     },
39566
39567     // private
39568     shouldDeactivate : function(e){
39569         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39570             if(this.menu && this.menu.isVisible()){
39571                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39572             }
39573             return true;
39574         }
39575         return false;
39576     },
39577
39578     // private
39579     deactivate : function(){
39580         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39581         this.hideMenu();
39582     },
39583
39584     // private
39585     expandMenu : function(autoActivate){
39586         if(!this.disabled && this.menu){
39587             clearTimeout(this.hideTimer);
39588             delete this.hideTimer;
39589             if(!this.menu.isVisible() && !this.showTimer){
39590                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39591             }else if (this.menu.isVisible() && autoActivate){
39592                 this.menu.tryActivate(0, 1);
39593             }
39594         }
39595     },
39596
39597     // private
39598     deferExpand : function(autoActivate){
39599         delete this.showTimer;
39600         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39601         if(autoActivate){
39602             this.menu.tryActivate(0, 1);
39603         }
39604     },
39605
39606     // private
39607     hideMenu : function(){
39608         clearTimeout(this.showTimer);
39609         delete this.showTimer;
39610         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39611             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39612         }
39613     },
39614
39615     // private
39616     deferHide : function(){
39617         delete this.hideTimer;
39618         this.menu.hide();
39619     }
39620 });/*
39621  * Based on:
39622  * Ext JS Library 1.1.1
39623  * Copyright(c) 2006-2007, Ext JS, LLC.
39624  *
39625  * Originally Released Under LGPL - original licence link has changed is not relivant.
39626  *
39627  * Fork - LGPL
39628  * <script type="text/javascript">
39629  */
39630  
39631 /**
39632  * @class Roo.menu.CheckItem
39633  * @extends Roo.menu.Item
39634  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39635  * @constructor
39636  * Creates a new CheckItem
39637  * @param {Object} config Configuration options
39638  */
39639 Roo.menu.CheckItem = function(config){
39640     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39641     this.addEvents({
39642         /**
39643          * @event beforecheckchange
39644          * Fires before the checked value is set, providing an opportunity to cancel if needed
39645          * @param {Roo.menu.CheckItem} this
39646          * @param {Boolean} checked The new checked value that will be set
39647          */
39648         "beforecheckchange" : true,
39649         /**
39650          * @event checkchange
39651          * Fires after the checked value has been set
39652          * @param {Roo.menu.CheckItem} this
39653          * @param {Boolean} checked The checked value that was set
39654          */
39655         "checkchange" : true
39656     });
39657     if(this.checkHandler){
39658         this.on('checkchange', this.checkHandler, this.scope);
39659     }
39660 };
39661 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39662     /**
39663      * @cfg {String} group
39664      * All check items with the same group name will automatically be grouped into a single-select
39665      * radio button group (defaults to '')
39666      */
39667     /**
39668      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39669      */
39670     itemCls : "x-menu-item x-menu-check-item",
39671     /**
39672      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39673      */
39674     groupClass : "x-menu-group-item",
39675
39676     /**
39677      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39678      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39679      * initialized with checked = true will be rendered as checked.
39680      */
39681     checked: false,
39682
39683     // private
39684     ctype: "Roo.menu.CheckItem",
39685
39686     // private
39687     onRender : function(c){
39688         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39689         if(this.group){
39690             this.el.addClass(this.groupClass);
39691         }
39692         Roo.menu.MenuMgr.registerCheckable(this);
39693         if(this.checked){
39694             this.checked = false;
39695             this.setChecked(true, true);
39696         }
39697     },
39698
39699     // private
39700     destroy : function(){
39701         if(this.rendered){
39702             Roo.menu.MenuMgr.unregisterCheckable(this);
39703         }
39704         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39705     },
39706
39707     /**
39708      * Set the checked state of this item
39709      * @param {Boolean} checked The new checked value
39710      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39711      */
39712     setChecked : function(state, suppressEvent){
39713         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39714             if(this.container){
39715                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39716             }
39717             this.checked = state;
39718             if(suppressEvent !== true){
39719                 this.fireEvent("checkchange", this, state);
39720             }
39721         }
39722     },
39723
39724     // private
39725     handleClick : function(e){
39726        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39727            this.setChecked(!this.checked);
39728        }
39729        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39730     }
39731 });/*
39732  * Based on:
39733  * Ext JS Library 1.1.1
39734  * Copyright(c) 2006-2007, Ext JS, LLC.
39735  *
39736  * Originally Released Under LGPL - original licence link has changed is not relivant.
39737  *
39738  * Fork - LGPL
39739  * <script type="text/javascript">
39740  */
39741  
39742 /**
39743  * @class Roo.menu.DateItem
39744  * @extends Roo.menu.Adapter
39745  * A menu item that wraps the {@link Roo.DatPicker} component.
39746  * @constructor
39747  * Creates a new DateItem
39748  * @param {Object} config Configuration options
39749  */
39750 Roo.menu.DateItem = function(config){
39751     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39752     /** The Roo.DatePicker object @type Roo.DatePicker */
39753     this.picker = this.component;
39754     this.addEvents({select: true});
39755     
39756     this.picker.on("render", function(picker){
39757         picker.getEl().swallowEvent("click");
39758         picker.container.addClass("x-menu-date-item");
39759     });
39760
39761     this.picker.on("select", this.onSelect, this);
39762 };
39763
39764 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39765     // private
39766     onSelect : function(picker, date){
39767         this.fireEvent("select", this, date, picker);
39768         Roo.menu.DateItem.superclass.handleClick.call(this);
39769     }
39770 });/*
39771  * Based on:
39772  * Ext JS Library 1.1.1
39773  * Copyright(c) 2006-2007, Ext JS, LLC.
39774  *
39775  * Originally Released Under LGPL - original licence link has changed is not relivant.
39776  *
39777  * Fork - LGPL
39778  * <script type="text/javascript">
39779  */
39780  
39781 /**
39782  * @class Roo.menu.ColorItem
39783  * @extends Roo.menu.Adapter
39784  * A menu item that wraps the {@link Roo.ColorPalette} component.
39785  * @constructor
39786  * Creates a new ColorItem
39787  * @param {Object} config Configuration options
39788  */
39789 Roo.menu.ColorItem = function(config){
39790     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39791     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39792     this.palette = this.component;
39793     this.relayEvents(this.palette, ["select"]);
39794     if(this.selectHandler){
39795         this.on('select', this.selectHandler, this.scope);
39796     }
39797 };
39798 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39799  * Based on:
39800  * Ext JS Library 1.1.1
39801  * Copyright(c) 2006-2007, Ext JS, LLC.
39802  *
39803  * Originally Released Under LGPL - original licence link has changed is not relivant.
39804  *
39805  * Fork - LGPL
39806  * <script type="text/javascript">
39807  */
39808  
39809
39810 /**
39811  * @class Roo.menu.DateMenu
39812  * @extends Roo.menu.Menu
39813  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39814  * @constructor
39815  * Creates a new DateMenu
39816  * @param {Object} config Configuration options
39817  */
39818 Roo.menu.DateMenu = function(config){
39819     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39820     this.plain = true;
39821     var di = new Roo.menu.DateItem(config);
39822     this.add(di);
39823     /**
39824      * The {@link Roo.DatePicker} instance for this DateMenu
39825      * @type DatePicker
39826      */
39827     this.picker = di.picker;
39828     /**
39829      * @event select
39830      * @param {DatePicker} picker
39831      * @param {Date} date
39832      */
39833     this.relayEvents(di, ["select"]);
39834     this.on('beforeshow', function(){
39835         if(this.picker){
39836             this.picker.hideMonthPicker(false);
39837         }
39838     }, this);
39839 };
39840 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39841     cls:'x-date-menu'
39842 });/*
39843  * Based on:
39844  * Ext JS Library 1.1.1
39845  * Copyright(c) 2006-2007, Ext JS, LLC.
39846  *
39847  * Originally Released Under LGPL - original licence link has changed is not relivant.
39848  *
39849  * Fork - LGPL
39850  * <script type="text/javascript">
39851  */
39852  
39853
39854 /**
39855  * @class Roo.menu.ColorMenu
39856  * @extends Roo.menu.Menu
39857  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39858  * @constructor
39859  * Creates a new ColorMenu
39860  * @param {Object} config Configuration options
39861  */
39862 Roo.menu.ColorMenu = function(config){
39863     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39864     this.plain = true;
39865     var ci = new Roo.menu.ColorItem(config);
39866     this.add(ci);
39867     /**
39868      * The {@link Roo.ColorPalette} instance for this ColorMenu
39869      * @type ColorPalette
39870      */
39871     this.palette = ci.palette;
39872     /**
39873      * @event select
39874      * @param {ColorPalette} palette
39875      * @param {String} color
39876      */
39877     this.relayEvents(ci, ["select"]);
39878 };
39879 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39880  * Based on:
39881  * Ext JS Library 1.1.1
39882  * Copyright(c) 2006-2007, Ext JS, LLC.
39883  *
39884  * Originally Released Under LGPL - original licence link has changed is not relivant.
39885  *
39886  * Fork - LGPL
39887  * <script type="text/javascript">
39888  */
39889  
39890 /**
39891  * @class Roo.form.TextItem
39892  * @extends Roo.BoxComponent
39893  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39894  * @constructor
39895  * Creates a new TextItem
39896  * @param {Object} config Configuration options
39897  */
39898 Roo.form.TextItem = function(config){
39899     Roo.form.TextItem.superclass.constructor.call(this, config);
39900 };
39901
39902 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39903     
39904     /**
39905      * @cfg {String} tag the tag for this item (default div)
39906      */
39907     tag : 'div',
39908     /**
39909      * @cfg {String} html the content for this item
39910      */
39911     html : '',
39912     
39913     getAutoCreate : function()
39914     {
39915         var cfg = {
39916             id: this.id,
39917             tag: this.tag,
39918             html: this.html,
39919             cls: 'x-form-item'
39920         };
39921         
39922         return cfg;
39923         
39924     },
39925     
39926     onRender : function(ct, position)
39927     {
39928         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39929         
39930         if(!this.el){
39931             var cfg = this.getAutoCreate();
39932             if(!cfg.name){
39933                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39934             }
39935             if (!cfg.name.length) {
39936                 delete cfg.name;
39937             }
39938             this.el = ct.createChild(cfg, position);
39939         }
39940     },
39941     /*
39942      * setHTML
39943      * @param {String} html update the Contents of the element.
39944      */
39945     setHTML : function(html)
39946     {
39947         this.fieldEl.dom.innerHTML = html;
39948     }
39949     
39950 });/*
39951  * Based on:
39952  * Ext JS Library 1.1.1
39953  * Copyright(c) 2006-2007, Ext JS, LLC.
39954  *
39955  * Originally Released Under LGPL - original licence link has changed is not relivant.
39956  *
39957  * Fork - LGPL
39958  * <script type="text/javascript">
39959  */
39960  
39961 /**
39962  * @class Roo.form.Field
39963  * @extends Roo.BoxComponent
39964  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39965  * @constructor
39966  * Creates a new Field
39967  * @param {Object} config Configuration options
39968  */
39969 Roo.form.Field = function(config){
39970     Roo.form.Field.superclass.constructor.call(this, config);
39971 };
39972
39973 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39974     /**
39975      * @cfg {String} fieldLabel Label to use when rendering a form.
39976      */
39977        /**
39978      * @cfg {String} qtip Mouse over tip
39979      */
39980      
39981     /**
39982      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39983      */
39984     invalidClass : "x-form-invalid",
39985     /**
39986      * @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")
39987      */
39988     invalidText : "The value in this field is invalid",
39989     /**
39990      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39991      */
39992     focusClass : "x-form-focus",
39993     /**
39994      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39995       automatic validation (defaults to "keyup").
39996      */
39997     validationEvent : "keyup",
39998     /**
39999      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40000      */
40001     validateOnBlur : true,
40002     /**
40003      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40004      */
40005     validationDelay : 250,
40006     /**
40007      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40008      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40009      */
40010     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40011     /**
40012      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40013      */
40014     fieldClass : "x-form-field",
40015     /**
40016      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40017      *<pre>
40018 Value         Description
40019 -----------   ----------------------------------------------------------------------
40020 qtip          Display a quick tip when the user hovers over the field
40021 title         Display a default browser title attribute popup
40022 under         Add a block div beneath the field containing the error text
40023 side          Add an error icon to the right of the field with a popup on hover
40024 [element id]  Add the error text directly to the innerHTML of the specified element
40025 </pre>
40026      */
40027     msgTarget : 'qtip',
40028     /**
40029      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40030      */
40031     msgFx : 'normal',
40032
40033     /**
40034      * @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.
40035      */
40036     readOnly : false,
40037
40038     /**
40039      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40040      */
40041     disabled : false,
40042
40043     /**
40044      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40045      */
40046     inputType : undefined,
40047     
40048     /**
40049      * @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).
40050          */
40051         tabIndex : undefined,
40052         
40053     // private
40054     isFormField : true,
40055
40056     // private
40057     hasFocus : false,
40058     /**
40059      * @property {Roo.Element} fieldEl
40060      * Element Containing the rendered Field (with label etc.)
40061      */
40062     /**
40063      * @cfg {Mixed} value A value to initialize this field with.
40064      */
40065     value : undefined,
40066
40067     /**
40068      * @cfg {String} name The field's HTML name attribute.
40069      */
40070     /**
40071      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40072      */
40073     // private
40074     loadedValue : false,
40075      
40076      
40077         // private ??
40078         initComponent : function(){
40079         Roo.form.Field.superclass.initComponent.call(this);
40080         this.addEvents({
40081             /**
40082              * @event focus
40083              * Fires when this field receives input focus.
40084              * @param {Roo.form.Field} this
40085              */
40086             focus : true,
40087             /**
40088              * @event blur
40089              * Fires when this field loses input focus.
40090              * @param {Roo.form.Field} this
40091              */
40092             blur : true,
40093             /**
40094              * @event specialkey
40095              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40096              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40097              * @param {Roo.form.Field} this
40098              * @param {Roo.EventObject} e The event object
40099              */
40100             specialkey : true,
40101             /**
40102              * @event change
40103              * Fires just before the field blurs if the field value has changed.
40104              * @param {Roo.form.Field} this
40105              * @param {Mixed} newValue The new value
40106              * @param {Mixed} oldValue The original value
40107              */
40108             change : true,
40109             /**
40110              * @event invalid
40111              * Fires after the field has been marked as invalid.
40112              * @param {Roo.form.Field} this
40113              * @param {String} msg The validation message
40114              */
40115             invalid : true,
40116             /**
40117              * @event valid
40118              * Fires after the field has been validated with no errors.
40119              * @param {Roo.form.Field} this
40120              */
40121             valid : true,
40122              /**
40123              * @event keyup
40124              * Fires after the key up
40125              * @param {Roo.form.Field} this
40126              * @param {Roo.EventObject}  e The event Object
40127              */
40128             keyup : true
40129         });
40130     },
40131
40132     /**
40133      * Returns the name attribute of the field if available
40134      * @return {String} name The field name
40135      */
40136     getName: function(){
40137          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40138     },
40139
40140     // private
40141     onRender : function(ct, position){
40142         Roo.form.Field.superclass.onRender.call(this, ct, position);
40143         if(!this.el){
40144             var cfg = this.getAutoCreate();
40145             if(!cfg.name){
40146                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40147             }
40148             if (!cfg.name.length) {
40149                 delete cfg.name;
40150             }
40151             if(this.inputType){
40152                 cfg.type = this.inputType;
40153             }
40154             this.el = ct.createChild(cfg, position);
40155         }
40156         var type = this.el.dom.type;
40157         if(type){
40158             if(type == 'password'){
40159                 type = 'text';
40160             }
40161             this.el.addClass('x-form-'+type);
40162         }
40163         if(this.readOnly){
40164             this.el.dom.readOnly = true;
40165         }
40166         if(this.tabIndex !== undefined){
40167             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40168         }
40169
40170         this.el.addClass([this.fieldClass, this.cls]);
40171         this.initValue();
40172     },
40173
40174     /**
40175      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40176      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40177      * @return {Roo.form.Field} this
40178      */
40179     applyTo : function(target){
40180         this.allowDomMove = false;
40181         this.el = Roo.get(target);
40182         this.render(this.el.dom.parentNode);
40183         return this;
40184     },
40185
40186     // private
40187     initValue : function(){
40188         if(this.value !== undefined){
40189             this.setValue(this.value);
40190         }else if(this.el.dom.value.length > 0){
40191             this.setValue(this.el.dom.value);
40192         }
40193     },
40194
40195     /**
40196      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40197      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40198      */
40199     isDirty : function() {
40200         if(this.disabled) {
40201             return false;
40202         }
40203         return String(this.getValue()) !== String(this.originalValue);
40204     },
40205
40206     /**
40207      * stores the current value in loadedValue
40208      */
40209     resetHasChanged : function()
40210     {
40211         this.loadedValue = String(this.getValue());
40212     },
40213     /**
40214      * checks the current value against the 'loaded' value.
40215      * Note - will return false if 'resetHasChanged' has not been called first.
40216      */
40217     hasChanged : function()
40218     {
40219         if(this.disabled || this.readOnly) {
40220             return false;
40221         }
40222         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40223     },
40224     
40225     
40226     
40227     // private
40228     afterRender : function(){
40229         Roo.form.Field.superclass.afterRender.call(this);
40230         this.initEvents();
40231     },
40232
40233     // private
40234     fireKey : function(e){
40235         //Roo.log('field ' + e.getKey());
40236         if(e.isNavKeyPress()){
40237             this.fireEvent("specialkey", this, e);
40238         }
40239     },
40240
40241     /**
40242      * Resets the current field value to the originally loaded value and clears any validation messages
40243      */
40244     reset : function(){
40245         this.setValue(this.resetValue);
40246         this.originalValue = this.getValue();
40247         this.clearInvalid();
40248     },
40249
40250     // private
40251     initEvents : function(){
40252         // safari killled keypress - so keydown is now used..
40253         this.el.on("keydown" , this.fireKey,  this);
40254         this.el.on("focus", this.onFocus,  this);
40255         this.el.on("blur", this.onBlur,  this);
40256         this.el.relayEvent('keyup', this);
40257
40258         // reference to original value for reset
40259         this.originalValue = this.getValue();
40260         this.resetValue =  this.getValue();
40261     },
40262
40263     // private
40264     onFocus : function(){
40265         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40266             this.el.addClass(this.focusClass);
40267         }
40268         if(!this.hasFocus){
40269             this.hasFocus = true;
40270             this.startValue = this.getValue();
40271             this.fireEvent("focus", this);
40272         }
40273     },
40274
40275     beforeBlur : Roo.emptyFn,
40276
40277     // private
40278     onBlur : function(){
40279         this.beforeBlur();
40280         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40281             this.el.removeClass(this.focusClass);
40282         }
40283         this.hasFocus = false;
40284         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40285             this.validate();
40286         }
40287         var v = this.getValue();
40288         if(String(v) !== String(this.startValue)){
40289             this.fireEvent('change', this, v, this.startValue);
40290         }
40291         this.fireEvent("blur", this);
40292     },
40293
40294     /**
40295      * Returns whether or not the field value is currently valid
40296      * @param {Boolean} preventMark True to disable marking the field invalid
40297      * @return {Boolean} True if the value is valid, else false
40298      */
40299     isValid : function(preventMark){
40300         if(this.disabled){
40301             return true;
40302         }
40303         var restore = this.preventMark;
40304         this.preventMark = preventMark === true;
40305         var v = this.validateValue(this.processValue(this.getRawValue()));
40306         this.preventMark = restore;
40307         return v;
40308     },
40309
40310     /**
40311      * Validates the field value
40312      * @return {Boolean} True if the value is valid, else false
40313      */
40314     validate : function(){
40315         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40316             this.clearInvalid();
40317             return true;
40318         }
40319         return false;
40320     },
40321
40322     processValue : function(value){
40323         return value;
40324     },
40325
40326     // private
40327     // Subclasses should provide the validation implementation by overriding this
40328     validateValue : function(value){
40329         return true;
40330     },
40331
40332     /**
40333      * Mark this field as invalid
40334      * @param {String} msg The validation message
40335      */
40336     markInvalid : function(msg){
40337         if(!this.rendered || this.preventMark){ // not rendered
40338             return;
40339         }
40340         
40341         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40342         
40343         obj.el.addClass(this.invalidClass);
40344         msg = msg || this.invalidText;
40345         switch(this.msgTarget){
40346             case 'qtip':
40347                 obj.el.dom.qtip = msg;
40348                 obj.el.dom.qclass = 'x-form-invalid-tip';
40349                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40350                     Roo.QuickTips.enable();
40351                 }
40352                 break;
40353             case 'title':
40354                 this.el.dom.title = msg;
40355                 break;
40356             case 'under':
40357                 if(!this.errorEl){
40358                     var elp = this.el.findParent('.x-form-element', 5, true);
40359                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40360                     this.errorEl.setWidth(elp.getWidth(true)-20);
40361                 }
40362                 this.errorEl.update(msg);
40363                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40364                 break;
40365             case 'side':
40366                 if(!this.errorIcon){
40367                     var elp = this.el.findParent('.x-form-element', 5, true);
40368                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40369                 }
40370                 this.alignErrorIcon();
40371                 this.errorIcon.dom.qtip = msg;
40372                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40373                 this.errorIcon.show();
40374                 this.on('resize', this.alignErrorIcon, this);
40375                 break;
40376             default:
40377                 var t = Roo.getDom(this.msgTarget);
40378                 t.innerHTML = msg;
40379                 t.style.display = this.msgDisplay;
40380                 break;
40381         }
40382         this.fireEvent('invalid', this, msg);
40383     },
40384
40385     // private
40386     alignErrorIcon : function(){
40387         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40388     },
40389
40390     /**
40391      * Clear any invalid styles/messages for this field
40392      */
40393     clearInvalid : function(){
40394         if(!this.rendered || this.preventMark){ // not rendered
40395             return;
40396         }
40397         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40398         
40399         obj.el.removeClass(this.invalidClass);
40400         switch(this.msgTarget){
40401             case 'qtip':
40402                 obj.el.dom.qtip = '';
40403                 break;
40404             case 'title':
40405                 this.el.dom.title = '';
40406                 break;
40407             case 'under':
40408                 if(this.errorEl){
40409                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40410                 }
40411                 break;
40412             case 'side':
40413                 if(this.errorIcon){
40414                     this.errorIcon.dom.qtip = '';
40415                     this.errorIcon.hide();
40416                     this.un('resize', this.alignErrorIcon, this);
40417                 }
40418                 break;
40419             default:
40420                 var t = Roo.getDom(this.msgTarget);
40421                 t.innerHTML = '';
40422                 t.style.display = 'none';
40423                 break;
40424         }
40425         this.fireEvent('valid', this);
40426     },
40427
40428     /**
40429      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40430      * @return {Mixed} value The field value
40431      */
40432     getRawValue : function(){
40433         var v = this.el.getValue();
40434         
40435         return v;
40436     },
40437
40438     /**
40439      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40440      * @return {Mixed} value The field value
40441      */
40442     getValue : function(){
40443         var v = this.el.getValue();
40444          
40445         return v;
40446     },
40447
40448     /**
40449      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40450      * @param {Mixed} value The value to set
40451      */
40452     setRawValue : function(v){
40453         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40454     },
40455
40456     /**
40457      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40458      * @param {Mixed} value The value to set
40459      */
40460     setValue : function(v){
40461         this.value = v;
40462         if(this.rendered){
40463             this.el.dom.value = (v === null || v === undefined ? '' : v);
40464              this.validate();
40465         }
40466     },
40467
40468     adjustSize : function(w, h){
40469         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40470         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40471         return s;
40472     },
40473
40474     adjustWidth : function(tag, w){
40475         tag = tag.toLowerCase();
40476         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40477             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40478                 if(tag == 'input'){
40479                     return w + 2;
40480                 }
40481                 if(tag == 'textarea'){
40482                     return w-2;
40483                 }
40484             }else if(Roo.isOpera){
40485                 if(tag == 'input'){
40486                     return w + 2;
40487                 }
40488                 if(tag == 'textarea'){
40489                     return w-2;
40490                 }
40491             }
40492         }
40493         return w;
40494     }
40495 });
40496
40497
40498 // anything other than normal should be considered experimental
40499 Roo.form.Field.msgFx = {
40500     normal : {
40501         show: function(msgEl, f){
40502             msgEl.setDisplayed('block');
40503         },
40504
40505         hide : function(msgEl, f){
40506             msgEl.setDisplayed(false).update('');
40507         }
40508     },
40509
40510     slide : {
40511         show: function(msgEl, f){
40512             msgEl.slideIn('t', {stopFx:true});
40513         },
40514
40515         hide : function(msgEl, f){
40516             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40517         }
40518     },
40519
40520     slideRight : {
40521         show: function(msgEl, f){
40522             msgEl.fixDisplay();
40523             msgEl.alignTo(f.el, 'tl-tr');
40524             msgEl.slideIn('l', {stopFx:true});
40525         },
40526
40527         hide : function(msgEl, f){
40528             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40529         }
40530     }
40531 };/*
40532  * Based on:
40533  * Ext JS Library 1.1.1
40534  * Copyright(c) 2006-2007, Ext JS, LLC.
40535  *
40536  * Originally Released Under LGPL - original licence link has changed is not relivant.
40537  *
40538  * Fork - LGPL
40539  * <script type="text/javascript">
40540  */
40541  
40542
40543 /**
40544  * @class Roo.form.TextField
40545  * @extends Roo.form.Field
40546  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40547  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40548  * @constructor
40549  * Creates a new TextField
40550  * @param {Object} config Configuration options
40551  */
40552 Roo.form.TextField = function(config){
40553     Roo.form.TextField.superclass.constructor.call(this, config);
40554     this.addEvents({
40555         /**
40556          * @event autosize
40557          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40558          * according to the default logic, but this event provides a hook for the developer to apply additional
40559          * logic at runtime to resize the field if needed.
40560              * @param {Roo.form.Field} this This text field
40561              * @param {Number} width The new field width
40562              */
40563         autosize : true
40564     });
40565 };
40566
40567 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40568     /**
40569      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40570      */
40571     grow : false,
40572     /**
40573      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40574      */
40575     growMin : 30,
40576     /**
40577      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40578      */
40579     growMax : 800,
40580     /**
40581      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40582      */
40583     vtype : null,
40584     /**
40585      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40586      */
40587     maskRe : null,
40588     /**
40589      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40590      */
40591     disableKeyFilter : false,
40592     /**
40593      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40594      */
40595     allowBlank : true,
40596     /**
40597      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40598      */
40599     minLength : 0,
40600     /**
40601      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40602      */
40603     maxLength : Number.MAX_VALUE,
40604     /**
40605      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40606      */
40607     minLengthText : "The minimum length for this field is {0}",
40608     /**
40609      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40610      */
40611     maxLengthText : "The maximum length for this field is {0}",
40612     /**
40613      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40614      */
40615     selectOnFocus : false,
40616     /**
40617      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40618      */    
40619     allowLeadingSpace : false,
40620     /**
40621      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40622      */
40623     blankText : "This field is required",
40624     /**
40625      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40626      * If available, this function will be called only after the basic validators all return true, and will be passed the
40627      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40628      */
40629     validator : null,
40630     /**
40631      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40632      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40633      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40634      */
40635     regex : null,
40636     /**
40637      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40638      */
40639     regexText : "",
40640     /**
40641      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40642      */
40643     emptyText : null,
40644    
40645
40646     // private
40647     initEvents : function()
40648     {
40649         if (this.emptyText) {
40650             this.el.attr('placeholder', this.emptyText);
40651         }
40652         
40653         Roo.form.TextField.superclass.initEvents.call(this);
40654         if(this.validationEvent == 'keyup'){
40655             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40656             this.el.on('keyup', this.filterValidation, this);
40657         }
40658         else if(this.validationEvent !== false){
40659             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40660         }
40661         
40662         if(this.selectOnFocus){
40663             this.on("focus", this.preFocus, this);
40664         }
40665         if (!this.allowLeadingSpace) {
40666             this.on('blur', this.cleanLeadingSpace, this);
40667         }
40668         
40669         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40670             this.el.on("keypress", this.filterKeys, this);
40671         }
40672         if(this.grow){
40673             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40674             this.el.on("click", this.autoSize,  this);
40675         }
40676         if(this.el.is('input[type=password]') && Roo.isSafari){
40677             this.el.on('keydown', this.SafariOnKeyDown, this);
40678         }
40679     },
40680
40681     processValue : function(value){
40682         if(this.stripCharsRe){
40683             var newValue = value.replace(this.stripCharsRe, '');
40684             if(newValue !== value){
40685                 this.setRawValue(newValue);
40686                 return newValue;
40687             }
40688         }
40689         return value;
40690     },
40691
40692     filterValidation : function(e){
40693         if(!e.isNavKeyPress()){
40694             this.validationTask.delay(this.validationDelay);
40695         }
40696     },
40697
40698     // private
40699     onKeyUp : function(e){
40700         if(!e.isNavKeyPress()){
40701             this.autoSize();
40702         }
40703     },
40704     // private - clean the leading white space
40705     cleanLeadingSpace : function(e)
40706     {
40707         if ( this.inputType == 'file') {
40708             return;
40709         }
40710         
40711         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40712     },
40713     /**
40714      * Resets the current field value to the originally-loaded value and clears any validation messages.
40715      *  
40716      */
40717     reset : function(){
40718         Roo.form.TextField.superclass.reset.call(this);
40719        
40720     }, 
40721     // private
40722     preFocus : function(){
40723         
40724         if(this.selectOnFocus){
40725             this.el.dom.select();
40726         }
40727     },
40728
40729     
40730     // private
40731     filterKeys : function(e){
40732         var k = e.getKey();
40733         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40734             return;
40735         }
40736         var c = e.getCharCode(), cc = String.fromCharCode(c);
40737         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40738             return;
40739         }
40740         if(!this.maskRe.test(cc)){
40741             e.stopEvent();
40742         }
40743     },
40744
40745     setValue : function(v){
40746         
40747         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40748         
40749         this.autoSize();
40750     },
40751
40752     /**
40753      * Validates a value according to the field's validation rules and marks the field as invalid
40754      * if the validation fails
40755      * @param {Mixed} value The value to validate
40756      * @return {Boolean} True if the value is valid, else false
40757      */
40758     validateValue : function(value){
40759         if(value.length < 1)  { // if it's blank
40760              if(this.allowBlank){
40761                 this.clearInvalid();
40762                 return true;
40763              }else{
40764                 this.markInvalid(this.blankText);
40765                 return false;
40766              }
40767         }
40768         if(value.length < this.minLength){
40769             this.markInvalid(String.format(this.minLengthText, this.minLength));
40770             return false;
40771         }
40772         if(value.length > this.maxLength){
40773             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40774             return false;
40775         }
40776         if(this.vtype){
40777             var vt = Roo.form.VTypes;
40778             if(!vt[this.vtype](value, this)){
40779                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40780                 return false;
40781             }
40782         }
40783         if(typeof this.validator == "function"){
40784             var msg = this.validator(value);
40785             if(msg !== true){
40786                 this.markInvalid(msg);
40787                 return false;
40788             }
40789         }
40790         if(this.regex && !this.regex.test(value)){
40791             this.markInvalid(this.regexText);
40792             return false;
40793         }
40794         return true;
40795     },
40796
40797     /**
40798      * Selects text in this field
40799      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40800      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40801      */
40802     selectText : function(start, end){
40803         var v = this.getRawValue();
40804         if(v.length > 0){
40805             start = start === undefined ? 0 : start;
40806             end = end === undefined ? v.length : end;
40807             var d = this.el.dom;
40808             if(d.setSelectionRange){
40809                 d.setSelectionRange(start, end);
40810             }else if(d.createTextRange){
40811                 var range = d.createTextRange();
40812                 range.moveStart("character", start);
40813                 range.moveEnd("character", v.length-end);
40814                 range.select();
40815             }
40816         }
40817     },
40818
40819     /**
40820      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40821      * This only takes effect if grow = true, and fires the autosize event.
40822      */
40823     autoSize : function(){
40824         if(!this.grow || !this.rendered){
40825             return;
40826         }
40827         if(!this.metrics){
40828             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40829         }
40830         var el = this.el;
40831         var v = el.dom.value;
40832         var d = document.createElement('div');
40833         d.appendChild(document.createTextNode(v));
40834         v = d.innerHTML;
40835         d = null;
40836         v += "&#160;";
40837         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40838         this.el.setWidth(w);
40839         this.fireEvent("autosize", this, w);
40840     },
40841     
40842     // private
40843     SafariOnKeyDown : function(event)
40844     {
40845         // this is a workaround for a password hang bug on chrome/ webkit.
40846         
40847         var isSelectAll = false;
40848         
40849         if(this.el.dom.selectionEnd > 0){
40850             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40851         }
40852         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40853             event.preventDefault();
40854             this.setValue('');
40855             return;
40856         }
40857         
40858         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40859             
40860             event.preventDefault();
40861             // this is very hacky as keydown always get's upper case.
40862             
40863             var cc = String.fromCharCode(event.getCharCode());
40864             
40865             
40866             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40867             
40868         }
40869         
40870         
40871     }
40872 });/*
40873  * Based on:
40874  * Ext JS Library 1.1.1
40875  * Copyright(c) 2006-2007, Ext JS, LLC.
40876  *
40877  * Originally Released Under LGPL - original licence link has changed is not relivant.
40878  *
40879  * Fork - LGPL
40880  * <script type="text/javascript">
40881  */
40882  
40883 /**
40884  * @class Roo.form.Hidden
40885  * @extends Roo.form.TextField
40886  * Simple Hidden element used on forms 
40887  * 
40888  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40889  * 
40890  * @constructor
40891  * Creates a new Hidden form element.
40892  * @param {Object} config Configuration options
40893  */
40894
40895
40896
40897 // easy hidden field...
40898 Roo.form.Hidden = function(config){
40899     Roo.form.Hidden.superclass.constructor.call(this, config);
40900 };
40901   
40902 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40903     fieldLabel:      '',
40904     inputType:      'hidden',
40905     width:          50,
40906     allowBlank:     true,
40907     labelSeparator: '',
40908     hidden:         true,
40909     itemCls :       'x-form-item-display-none'
40910
40911
40912 });
40913
40914
40915 /*
40916  * Based on:
40917  * Ext JS Library 1.1.1
40918  * Copyright(c) 2006-2007, Ext JS, LLC.
40919  *
40920  * Originally Released Under LGPL - original licence link has changed is not relivant.
40921  *
40922  * Fork - LGPL
40923  * <script type="text/javascript">
40924  */
40925  
40926 /**
40927  * @class Roo.form.TriggerField
40928  * @extends Roo.form.TextField
40929  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40930  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40931  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40932  * for which you can provide a custom implementation.  For example:
40933  * <pre><code>
40934 var trigger = new Roo.form.TriggerField();
40935 trigger.onTriggerClick = myTriggerFn;
40936 trigger.applyTo('my-field');
40937 </code></pre>
40938  *
40939  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40940  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40941  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40942  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40943  * @constructor
40944  * Create a new TriggerField.
40945  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40946  * to the base TextField)
40947  */
40948 Roo.form.TriggerField = function(config){
40949     this.mimicing = false;
40950     Roo.form.TriggerField.superclass.constructor.call(this, config);
40951 };
40952
40953 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40954     /**
40955      * @cfg {String} triggerClass A CSS class to apply to the trigger
40956      */
40957     /**
40958      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40959      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40960      */
40961     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40962     /**
40963      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40964      */
40965     hideTrigger:false,
40966
40967     /** @cfg {Boolean} grow @hide */
40968     /** @cfg {Number} growMin @hide */
40969     /** @cfg {Number} growMax @hide */
40970
40971     /**
40972      * @hide 
40973      * @method
40974      */
40975     autoSize: Roo.emptyFn,
40976     // private
40977     monitorTab : true,
40978     // private
40979     deferHeight : true,
40980
40981     
40982     actionMode : 'wrap',
40983     // private
40984     onResize : function(w, h){
40985         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40986         if(typeof w == 'number'){
40987             var x = w - this.trigger.getWidth();
40988             this.el.setWidth(this.adjustWidth('input', x));
40989             this.trigger.setStyle('left', x+'px');
40990         }
40991     },
40992
40993     // private
40994     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40995
40996     // private
40997     getResizeEl : function(){
40998         return this.wrap;
40999     },
41000
41001     // private
41002     getPositionEl : function(){
41003         return this.wrap;
41004     },
41005
41006     // private
41007     alignErrorIcon : function(){
41008         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41009     },
41010
41011     // private
41012     onRender : function(ct, position){
41013         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41014         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41015         this.trigger = this.wrap.createChild(this.triggerConfig ||
41016                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41017         if(this.hideTrigger){
41018             this.trigger.setDisplayed(false);
41019         }
41020         this.initTrigger();
41021         if(!this.width){
41022             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41023         }
41024     },
41025
41026     // private
41027     initTrigger : function(){
41028         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41029         this.trigger.addClassOnOver('x-form-trigger-over');
41030         this.trigger.addClassOnClick('x-form-trigger-click');
41031     },
41032
41033     // private
41034     onDestroy : function(){
41035         if(this.trigger){
41036             this.trigger.removeAllListeners();
41037             this.trigger.remove();
41038         }
41039         if(this.wrap){
41040             this.wrap.remove();
41041         }
41042         Roo.form.TriggerField.superclass.onDestroy.call(this);
41043     },
41044
41045     // private
41046     onFocus : function(){
41047         Roo.form.TriggerField.superclass.onFocus.call(this);
41048         if(!this.mimicing){
41049             this.wrap.addClass('x-trigger-wrap-focus');
41050             this.mimicing = true;
41051             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41052             if(this.monitorTab){
41053                 this.el.on("keydown", this.checkTab, this);
41054             }
41055         }
41056     },
41057
41058     // private
41059     checkTab : function(e){
41060         if(e.getKey() == e.TAB){
41061             this.triggerBlur();
41062         }
41063     },
41064
41065     // private
41066     onBlur : function(){
41067         // do nothing
41068     },
41069
41070     // private
41071     mimicBlur : function(e, t){
41072         if(!this.wrap.contains(t) && this.validateBlur()){
41073             this.triggerBlur();
41074         }
41075     },
41076
41077     // private
41078     triggerBlur : function(){
41079         this.mimicing = false;
41080         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41081         if(this.monitorTab){
41082             this.el.un("keydown", this.checkTab, this);
41083         }
41084         this.wrap.removeClass('x-trigger-wrap-focus');
41085         Roo.form.TriggerField.superclass.onBlur.call(this);
41086     },
41087
41088     // private
41089     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41090     validateBlur : function(e, t){
41091         return true;
41092     },
41093
41094     // private
41095     onDisable : function(){
41096         Roo.form.TriggerField.superclass.onDisable.call(this);
41097         if(this.wrap){
41098             this.wrap.addClass('x-item-disabled');
41099         }
41100     },
41101
41102     // private
41103     onEnable : function(){
41104         Roo.form.TriggerField.superclass.onEnable.call(this);
41105         if(this.wrap){
41106             this.wrap.removeClass('x-item-disabled');
41107         }
41108     },
41109
41110     // private
41111     onShow : function(){
41112         var ae = this.getActionEl();
41113         
41114         if(ae){
41115             ae.dom.style.display = '';
41116             ae.dom.style.visibility = 'visible';
41117         }
41118     },
41119
41120     // private
41121     
41122     onHide : function(){
41123         var ae = this.getActionEl();
41124         ae.dom.style.display = 'none';
41125     },
41126
41127     /**
41128      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41129      * by an implementing function.
41130      * @method
41131      * @param {EventObject} e
41132      */
41133     onTriggerClick : Roo.emptyFn
41134 });
41135
41136 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41137 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41138 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41139 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41140     initComponent : function(){
41141         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41142
41143         this.triggerConfig = {
41144             tag:'span', cls:'x-form-twin-triggers', cn:[
41145             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41146             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41147         ]};
41148     },
41149
41150     getTrigger : function(index){
41151         return this.triggers[index];
41152     },
41153
41154     initTrigger : function(){
41155         var ts = this.trigger.select('.x-form-trigger', true);
41156         this.wrap.setStyle('overflow', 'hidden');
41157         var triggerField = this;
41158         ts.each(function(t, all, index){
41159             t.hide = function(){
41160                 var w = triggerField.wrap.getWidth();
41161                 this.dom.style.display = 'none';
41162                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41163             };
41164             t.show = function(){
41165                 var w = triggerField.wrap.getWidth();
41166                 this.dom.style.display = '';
41167                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41168             };
41169             var triggerIndex = 'Trigger'+(index+1);
41170
41171             if(this['hide'+triggerIndex]){
41172                 t.dom.style.display = 'none';
41173             }
41174             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41175             t.addClassOnOver('x-form-trigger-over');
41176             t.addClassOnClick('x-form-trigger-click');
41177         }, this);
41178         this.triggers = ts.elements;
41179     },
41180
41181     onTrigger1Click : Roo.emptyFn,
41182     onTrigger2Click : Roo.emptyFn
41183 });/*
41184  * Based on:
41185  * Ext JS Library 1.1.1
41186  * Copyright(c) 2006-2007, Ext JS, LLC.
41187  *
41188  * Originally Released Under LGPL - original licence link has changed is not relivant.
41189  *
41190  * Fork - LGPL
41191  * <script type="text/javascript">
41192  */
41193  
41194 /**
41195  * @class Roo.form.TextArea
41196  * @extends Roo.form.TextField
41197  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41198  * support for auto-sizing.
41199  * @constructor
41200  * Creates a new TextArea
41201  * @param {Object} config Configuration options
41202  */
41203 Roo.form.TextArea = function(config){
41204     Roo.form.TextArea.superclass.constructor.call(this, config);
41205     // these are provided exchanges for backwards compat
41206     // minHeight/maxHeight were replaced by growMin/growMax to be
41207     // compatible with TextField growing config values
41208     if(this.minHeight !== undefined){
41209         this.growMin = this.minHeight;
41210     }
41211     if(this.maxHeight !== undefined){
41212         this.growMax = this.maxHeight;
41213     }
41214 };
41215
41216 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41217     /**
41218      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41219      */
41220     growMin : 60,
41221     /**
41222      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41223      */
41224     growMax: 1000,
41225     /**
41226      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41227      * in the field (equivalent to setting overflow: hidden, defaults to false)
41228      */
41229     preventScrollbars: false,
41230     /**
41231      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41232      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41233      */
41234
41235     // private
41236     onRender : function(ct, position){
41237         if(!this.el){
41238             this.defaultAutoCreate = {
41239                 tag: "textarea",
41240                 style:"width:300px;height:60px;",
41241                 autocomplete: "new-password"
41242             };
41243         }
41244         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41245         if(this.grow){
41246             this.textSizeEl = Roo.DomHelper.append(document.body, {
41247                 tag: "pre", cls: "x-form-grow-sizer"
41248             });
41249             if(this.preventScrollbars){
41250                 this.el.setStyle("overflow", "hidden");
41251             }
41252             this.el.setHeight(this.growMin);
41253         }
41254     },
41255
41256     onDestroy : function(){
41257         if(this.textSizeEl){
41258             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41259         }
41260         Roo.form.TextArea.superclass.onDestroy.call(this);
41261     },
41262
41263     // private
41264     onKeyUp : function(e){
41265         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41266             this.autoSize();
41267         }
41268     },
41269
41270     /**
41271      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41272      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41273      */
41274     autoSize : function(){
41275         if(!this.grow || !this.textSizeEl){
41276             return;
41277         }
41278         var el = this.el;
41279         var v = el.dom.value;
41280         var ts = this.textSizeEl;
41281
41282         ts.innerHTML = '';
41283         ts.appendChild(document.createTextNode(v));
41284         v = ts.innerHTML;
41285
41286         Roo.fly(ts).setWidth(this.el.getWidth());
41287         if(v.length < 1){
41288             v = "&#160;&#160;";
41289         }else{
41290             if(Roo.isIE){
41291                 v = v.replace(/\n/g, '<p>&#160;</p>');
41292             }
41293             v += "&#160;\n&#160;";
41294         }
41295         ts.innerHTML = v;
41296         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41297         if(h != this.lastHeight){
41298             this.lastHeight = h;
41299             this.el.setHeight(h);
41300             this.fireEvent("autosize", this, h);
41301         }
41302     }
41303 });/*
41304  * Based on:
41305  * Ext JS Library 1.1.1
41306  * Copyright(c) 2006-2007, Ext JS, LLC.
41307  *
41308  * Originally Released Under LGPL - original licence link has changed is not relivant.
41309  *
41310  * Fork - LGPL
41311  * <script type="text/javascript">
41312  */
41313  
41314
41315 /**
41316  * @class Roo.form.NumberField
41317  * @extends Roo.form.TextField
41318  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41319  * @constructor
41320  * Creates a new NumberField
41321  * @param {Object} config Configuration options
41322  */
41323 Roo.form.NumberField = function(config){
41324     Roo.form.NumberField.superclass.constructor.call(this, config);
41325 };
41326
41327 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41328     /**
41329      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41330      */
41331     fieldClass: "x-form-field x-form-num-field",
41332     /**
41333      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41334      */
41335     allowDecimals : true,
41336     /**
41337      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41338      */
41339     decimalSeparator : ".",
41340     /**
41341      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41342      */
41343     decimalPrecision : 2,
41344     /**
41345      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41346      */
41347     allowNegative : true,
41348     /**
41349      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41350      */
41351     minValue : Number.NEGATIVE_INFINITY,
41352     /**
41353      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41354      */
41355     maxValue : Number.MAX_VALUE,
41356     /**
41357      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41358      */
41359     minText : "The minimum value for this field is {0}",
41360     /**
41361      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41362      */
41363     maxText : "The maximum value for this field is {0}",
41364     /**
41365      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41366      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41367      */
41368     nanText : "{0} is not a valid number",
41369
41370     // private
41371     initEvents : function(){
41372         Roo.form.NumberField.superclass.initEvents.call(this);
41373         var allowed = "0123456789";
41374         if(this.allowDecimals){
41375             allowed += this.decimalSeparator;
41376         }
41377         if(this.allowNegative){
41378             allowed += "-";
41379         }
41380         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41381         var keyPress = function(e){
41382             var k = e.getKey();
41383             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41384                 return;
41385             }
41386             var c = e.getCharCode();
41387             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41388                 e.stopEvent();
41389             }
41390         };
41391         this.el.on("keypress", keyPress, this);
41392     },
41393
41394     // private
41395     validateValue : function(value){
41396         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41397             return false;
41398         }
41399         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41400              return true;
41401         }
41402         var num = this.parseValue(value);
41403         if(isNaN(num)){
41404             this.markInvalid(String.format(this.nanText, value));
41405             return false;
41406         }
41407         if(num < this.minValue){
41408             this.markInvalid(String.format(this.minText, this.minValue));
41409             return false;
41410         }
41411         if(num > this.maxValue){
41412             this.markInvalid(String.format(this.maxText, this.maxValue));
41413             return false;
41414         }
41415         return true;
41416     },
41417
41418     getValue : function(){
41419         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41420     },
41421
41422     // private
41423     parseValue : function(value){
41424         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41425         return isNaN(value) ? '' : value;
41426     },
41427
41428     // private
41429     fixPrecision : function(value){
41430         var nan = isNaN(value);
41431         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41432             return nan ? '' : value;
41433         }
41434         return parseFloat(value).toFixed(this.decimalPrecision);
41435     },
41436
41437     setValue : function(v){
41438         v = this.fixPrecision(v);
41439         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41440     },
41441
41442     // private
41443     decimalPrecisionFcn : function(v){
41444         return Math.floor(v);
41445     },
41446
41447     beforeBlur : function(){
41448         var v = this.parseValue(this.getRawValue());
41449         if(v){
41450             this.setValue(v);
41451         }
41452     }
41453 });/*
41454  * Based on:
41455  * Ext JS Library 1.1.1
41456  * Copyright(c) 2006-2007, Ext JS, LLC.
41457  *
41458  * Originally Released Under LGPL - original licence link has changed is not relivant.
41459  *
41460  * Fork - LGPL
41461  * <script type="text/javascript">
41462  */
41463  
41464 /**
41465  * @class Roo.form.DateField
41466  * @extends Roo.form.TriggerField
41467  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41468 * @constructor
41469 * Create a new DateField
41470 * @param {Object} config
41471  */
41472 Roo.form.DateField = function(config)
41473 {
41474     Roo.form.DateField.superclass.constructor.call(this, config);
41475     
41476       this.addEvents({
41477          
41478         /**
41479          * @event select
41480          * Fires when a date is selected
41481              * @param {Roo.form.DateField} combo This combo box
41482              * @param {Date} date The date selected
41483              */
41484         'select' : true
41485          
41486     });
41487     
41488     
41489     if(typeof this.minValue == "string") {
41490         this.minValue = this.parseDate(this.minValue);
41491     }
41492     if(typeof this.maxValue == "string") {
41493         this.maxValue = this.parseDate(this.maxValue);
41494     }
41495     this.ddMatch = null;
41496     if(this.disabledDates){
41497         var dd = this.disabledDates;
41498         var re = "(?:";
41499         for(var i = 0; i < dd.length; i++){
41500             re += dd[i];
41501             if(i != dd.length-1) {
41502                 re += "|";
41503             }
41504         }
41505         this.ddMatch = new RegExp(re + ")");
41506     }
41507 };
41508
41509 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41510     /**
41511      * @cfg {String} format
41512      * The default date format string which can be overriden for localization support.  The format must be
41513      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41514      */
41515     format : "m/d/y",
41516     /**
41517      * @cfg {String} altFormats
41518      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41519      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41520      */
41521     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41522     /**
41523      * @cfg {Array} disabledDays
41524      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41525      */
41526     disabledDays : null,
41527     /**
41528      * @cfg {String} disabledDaysText
41529      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41530      */
41531     disabledDaysText : "Disabled",
41532     /**
41533      * @cfg {Array} disabledDates
41534      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41535      * expression so they are very powerful. Some examples:
41536      * <ul>
41537      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41538      * <li>["03/08", "09/16"] would disable those days for every year</li>
41539      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41540      * <li>["03/../2006"] would disable every day in March 2006</li>
41541      * <li>["^03"] would disable every day in every March</li>
41542      * </ul>
41543      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41544      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41545      */
41546     disabledDates : null,
41547     /**
41548      * @cfg {String} disabledDatesText
41549      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41550      */
41551     disabledDatesText : "Disabled",
41552     /**
41553      * @cfg {Date/String} minValue
41554      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41555      * valid format (defaults to null).
41556      */
41557     minValue : null,
41558     /**
41559      * @cfg {Date/String} maxValue
41560      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41561      * valid format (defaults to null).
41562      */
41563     maxValue : null,
41564     /**
41565      * @cfg {String} minText
41566      * The error text to display when the date in the cell is before minValue (defaults to
41567      * 'The date in this field must be after {minValue}').
41568      */
41569     minText : "The date in this field must be equal to or after {0}",
41570     /**
41571      * @cfg {String} maxText
41572      * The error text to display when the date in the cell is after maxValue (defaults to
41573      * 'The date in this field must be before {maxValue}').
41574      */
41575     maxText : "The date in this field must be equal to or before {0}",
41576     /**
41577      * @cfg {String} invalidText
41578      * The error text to display when the date in the field is invalid (defaults to
41579      * '{value} is not a valid date - it must be in the format {format}').
41580      */
41581     invalidText : "{0} is not a valid date - it must be in the format {1}",
41582     /**
41583      * @cfg {String} triggerClass
41584      * An additional CSS class used to style the trigger button.  The trigger will always get the
41585      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41586      * which displays a calendar icon).
41587      */
41588     triggerClass : 'x-form-date-trigger',
41589     
41590
41591     /**
41592      * @cfg {Boolean} useIso
41593      * if enabled, then the date field will use a hidden field to store the 
41594      * real value as iso formated date. default (false)
41595      */ 
41596     useIso : false,
41597     /**
41598      * @cfg {String/Object} autoCreate
41599      * A DomHelper element spec, or true for a default element spec (defaults to
41600      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41601      */ 
41602     // private
41603     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41604     
41605     // private
41606     hiddenField: false,
41607     
41608     onRender : function(ct, position)
41609     {
41610         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41611         if (this.useIso) {
41612             //this.el.dom.removeAttribute('name'); 
41613             Roo.log("Changing name?");
41614             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41615             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41616                     'before', true);
41617             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41618             // prevent input submission
41619             this.hiddenName = this.name;
41620         }
41621             
41622             
41623     },
41624     
41625     // private
41626     validateValue : function(value)
41627     {
41628         value = this.formatDate(value);
41629         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41630             Roo.log('super failed');
41631             return false;
41632         }
41633         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41634              return true;
41635         }
41636         var svalue = value;
41637         value = this.parseDate(value);
41638         if(!value){
41639             Roo.log('parse date failed' + svalue);
41640             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41641             return false;
41642         }
41643         var time = value.getTime();
41644         if(this.minValue && time < this.minValue.getTime()){
41645             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41646             return false;
41647         }
41648         if(this.maxValue && time > this.maxValue.getTime()){
41649             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41650             return false;
41651         }
41652         if(this.disabledDays){
41653             var day = value.getDay();
41654             for(var i = 0; i < this.disabledDays.length; i++) {
41655                 if(day === this.disabledDays[i]){
41656                     this.markInvalid(this.disabledDaysText);
41657                     return false;
41658                 }
41659             }
41660         }
41661         var fvalue = this.formatDate(value);
41662         if(this.ddMatch && this.ddMatch.test(fvalue)){
41663             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41664             return false;
41665         }
41666         return true;
41667     },
41668
41669     // private
41670     // Provides logic to override the default TriggerField.validateBlur which just returns true
41671     validateBlur : function(){
41672         return !this.menu || !this.menu.isVisible();
41673     },
41674     
41675     getName: function()
41676     {
41677         // returns hidden if it's set..
41678         if (!this.rendered) {return ''};
41679         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41680         
41681     },
41682
41683     /**
41684      * Returns the current date value of the date field.
41685      * @return {Date} The date value
41686      */
41687     getValue : function(){
41688         
41689         return  this.hiddenField ?
41690                 this.hiddenField.value :
41691                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41692     },
41693
41694     /**
41695      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41696      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41697      * (the default format used is "m/d/y").
41698      * <br />Usage:
41699      * <pre><code>
41700 //All of these calls set the same date value (May 4, 2006)
41701
41702 //Pass a date object:
41703 var dt = new Date('5/4/06');
41704 dateField.setValue(dt);
41705
41706 //Pass a date string (default format):
41707 dateField.setValue('5/4/06');
41708
41709 //Pass a date string (custom format):
41710 dateField.format = 'Y-m-d';
41711 dateField.setValue('2006-5-4');
41712 </code></pre>
41713      * @param {String/Date} date The date or valid date string
41714      */
41715     setValue : function(date){
41716         if (this.hiddenField) {
41717             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41718         }
41719         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41720         // make sure the value field is always stored as a date..
41721         this.value = this.parseDate(date);
41722         
41723         
41724     },
41725
41726     // private
41727     parseDate : function(value){
41728         if(!value || value instanceof Date){
41729             return value;
41730         }
41731         var v = Date.parseDate(value, this.format);
41732          if (!v && this.useIso) {
41733             v = Date.parseDate(value, 'Y-m-d');
41734         }
41735         if(!v && this.altFormats){
41736             if(!this.altFormatsArray){
41737                 this.altFormatsArray = this.altFormats.split("|");
41738             }
41739             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41740                 v = Date.parseDate(value, this.altFormatsArray[i]);
41741             }
41742         }
41743         return v;
41744     },
41745
41746     // private
41747     formatDate : function(date, fmt){
41748         return (!date || !(date instanceof Date)) ?
41749                date : date.dateFormat(fmt || this.format);
41750     },
41751
41752     // private
41753     menuListeners : {
41754         select: function(m, d){
41755             
41756             this.setValue(d);
41757             this.fireEvent('select', this, d);
41758         },
41759         show : function(){ // retain focus styling
41760             this.onFocus();
41761         },
41762         hide : function(){
41763             this.focus.defer(10, this);
41764             var ml = this.menuListeners;
41765             this.menu.un("select", ml.select,  this);
41766             this.menu.un("show", ml.show,  this);
41767             this.menu.un("hide", ml.hide,  this);
41768         }
41769     },
41770
41771     // private
41772     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41773     onTriggerClick : function(){
41774         if(this.disabled){
41775             return;
41776         }
41777         if(this.menu == null){
41778             this.menu = new Roo.menu.DateMenu();
41779         }
41780         Roo.apply(this.menu.picker,  {
41781             showClear: this.allowBlank,
41782             minDate : this.minValue,
41783             maxDate : this.maxValue,
41784             disabledDatesRE : this.ddMatch,
41785             disabledDatesText : this.disabledDatesText,
41786             disabledDays : this.disabledDays,
41787             disabledDaysText : this.disabledDaysText,
41788             format : this.useIso ? 'Y-m-d' : this.format,
41789             minText : String.format(this.minText, this.formatDate(this.minValue)),
41790             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41791         });
41792         this.menu.on(Roo.apply({}, this.menuListeners, {
41793             scope:this
41794         }));
41795         this.menu.picker.setValue(this.getValue() || new Date());
41796         this.menu.show(this.el, "tl-bl?");
41797     },
41798
41799     beforeBlur : function(){
41800         var v = this.parseDate(this.getRawValue());
41801         if(v){
41802             this.setValue(v);
41803         }
41804     },
41805
41806     /*@
41807      * overide
41808      * 
41809      */
41810     isDirty : function() {
41811         if(this.disabled) {
41812             return false;
41813         }
41814         
41815         if(typeof(this.startValue) === 'undefined'){
41816             return false;
41817         }
41818         
41819         return String(this.getValue()) !== String(this.startValue);
41820         
41821     },
41822     // @overide
41823     cleanLeadingSpace : function(e)
41824     {
41825        return;
41826     }
41827     
41828 });/*
41829  * Based on:
41830  * Ext JS Library 1.1.1
41831  * Copyright(c) 2006-2007, Ext JS, LLC.
41832  *
41833  * Originally Released Under LGPL - original licence link has changed is not relivant.
41834  *
41835  * Fork - LGPL
41836  * <script type="text/javascript">
41837  */
41838  
41839 /**
41840  * @class Roo.form.MonthField
41841  * @extends Roo.form.TriggerField
41842  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41843 * @constructor
41844 * Create a new MonthField
41845 * @param {Object} config
41846  */
41847 Roo.form.MonthField = function(config){
41848     
41849     Roo.form.MonthField.superclass.constructor.call(this, config);
41850     
41851       this.addEvents({
41852          
41853         /**
41854          * @event select
41855          * Fires when a date is selected
41856              * @param {Roo.form.MonthFieeld} combo This combo box
41857              * @param {Date} date The date selected
41858              */
41859         'select' : true
41860          
41861     });
41862     
41863     
41864     if(typeof this.minValue == "string") {
41865         this.minValue = this.parseDate(this.minValue);
41866     }
41867     if(typeof this.maxValue == "string") {
41868         this.maxValue = this.parseDate(this.maxValue);
41869     }
41870     this.ddMatch = null;
41871     if(this.disabledDates){
41872         var dd = this.disabledDates;
41873         var re = "(?:";
41874         for(var i = 0; i < dd.length; i++){
41875             re += dd[i];
41876             if(i != dd.length-1) {
41877                 re += "|";
41878             }
41879         }
41880         this.ddMatch = new RegExp(re + ")");
41881     }
41882 };
41883
41884 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41885     /**
41886      * @cfg {String} format
41887      * The default date format string which can be overriden for localization support.  The format must be
41888      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41889      */
41890     format : "M Y",
41891     /**
41892      * @cfg {String} altFormats
41893      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41894      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41895      */
41896     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41897     /**
41898      * @cfg {Array} disabledDays
41899      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41900      */
41901     disabledDays : [0,1,2,3,4,5,6],
41902     /**
41903      * @cfg {String} disabledDaysText
41904      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41905      */
41906     disabledDaysText : "Disabled",
41907     /**
41908      * @cfg {Array} disabledDates
41909      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41910      * expression so they are very powerful. Some examples:
41911      * <ul>
41912      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41913      * <li>["03/08", "09/16"] would disable those days for every year</li>
41914      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41915      * <li>["03/../2006"] would disable every day in March 2006</li>
41916      * <li>["^03"] would disable every day in every March</li>
41917      * </ul>
41918      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41919      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41920      */
41921     disabledDates : null,
41922     /**
41923      * @cfg {String} disabledDatesText
41924      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41925      */
41926     disabledDatesText : "Disabled",
41927     /**
41928      * @cfg {Date/String} minValue
41929      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41930      * valid format (defaults to null).
41931      */
41932     minValue : null,
41933     /**
41934      * @cfg {Date/String} maxValue
41935      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41936      * valid format (defaults to null).
41937      */
41938     maxValue : null,
41939     /**
41940      * @cfg {String} minText
41941      * The error text to display when the date in the cell is before minValue (defaults to
41942      * 'The date in this field must be after {minValue}').
41943      */
41944     minText : "The date in this field must be equal to or after {0}",
41945     /**
41946      * @cfg {String} maxTextf
41947      * The error text to display when the date in the cell is after maxValue (defaults to
41948      * 'The date in this field must be before {maxValue}').
41949      */
41950     maxText : "The date in this field must be equal to or before {0}",
41951     /**
41952      * @cfg {String} invalidText
41953      * The error text to display when the date in the field is invalid (defaults to
41954      * '{value} is not a valid date - it must be in the format {format}').
41955      */
41956     invalidText : "{0} is not a valid date - it must be in the format {1}",
41957     /**
41958      * @cfg {String} triggerClass
41959      * An additional CSS class used to style the trigger button.  The trigger will always get the
41960      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41961      * which displays a calendar icon).
41962      */
41963     triggerClass : 'x-form-date-trigger',
41964     
41965
41966     /**
41967      * @cfg {Boolean} useIso
41968      * if enabled, then the date field will use a hidden field to store the 
41969      * real value as iso formated date. default (true)
41970      */ 
41971     useIso : true,
41972     /**
41973      * @cfg {String/Object} autoCreate
41974      * A DomHelper element spec, or true for a default element spec (defaults to
41975      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41976      */ 
41977     // private
41978     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41979     
41980     // private
41981     hiddenField: false,
41982     
41983     hideMonthPicker : false,
41984     
41985     onRender : function(ct, position)
41986     {
41987         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41988         if (this.useIso) {
41989             this.el.dom.removeAttribute('name'); 
41990             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41991                     'before', true);
41992             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41993             // prevent input submission
41994             this.hiddenName = this.name;
41995         }
41996             
41997             
41998     },
41999     
42000     // private
42001     validateValue : function(value)
42002     {
42003         value = this.formatDate(value);
42004         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42005             return false;
42006         }
42007         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42008              return true;
42009         }
42010         var svalue = value;
42011         value = this.parseDate(value);
42012         if(!value){
42013             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42014             return false;
42015         }
42016         var time = value.getTime();
42017         if(this.minValue && time < this.minValue.getTime()){
42018             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42019             return false;
42020         }
42021         if(this.maxValue && time > this.maxValue.getTime()){
42022             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42023             return false;
42024         }
42025         /*if(this.disabledDays){
42026             var day = value.getDay();
42027             for(var i = 0; i < this.disabledDays.length; i++) {
42028                 if(day === this.disabledDays[i]){
42029                     this.markInvalid(this.disabledDaysText);
42030                     return false;
42031                 }
42032             }
42033         }
42034         */
42035         var fvalue = this.formatDate(value);
42036         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42037             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42038             return false;
42039         }
42040         */
42041         return true;
42042     },
42043
42044     // private
42045     // Provides logic to override the default TriggerField.validateBlur which just returns true
42046     validateBlur : function(){
42047         return !this.menu || !this.menu.isVisible();
42048     },
42049
42050     /**
42051      * Returns the current date value of the date field.
42052      * @return {Date} The date value
42053      */
42054     getValue : function(){
42055         
42056         
42057         
42058         return  this.hiddenField ?
42059                 this.hiddenField.value :
42060                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42061     },
42062
42063     /**
42064      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42065      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42066      * (the default format used is "m/d/y").
42067      * <br />Usage:
42068      * <pre><code>
42069 //All of these calls set the same date value (May 4, 2006)
42070
42071 //Pass a date object:
42072 var dt = new Date('5/4/06');
42073 monthField.setValue(dt);
42074
42075 //Pass a date string (default format):
42076 monthField.setValue('5/4/06');
42077
42078 //Pass a date string (custom format):
42079 monthField.format = 'Y-m-d';
42080 monthField.setValue('2006-5-4');
42081 </code></pre>
42082      * @param {String/Date} date The date or valid date string
42083      */
42084     setValue : function(date){
42085         Roo.log('month setValue' + date);
42086         // can only be first of month..
42087         
42088         var val = this.parseDate(date);
42089         
42090         if (this.hiddenField) {
42091             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42092         }
42093         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42094         this.value = this.parseDate(date);
42095     },
42096
42097     // private
42098     parseDate : function(value){
42099         if(!value || value instanceof Date){
42100             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42101             return value;
42102         }
42103         var v = Date.parseDate(value, this.format);
42104         if (!v && this.useIso) {
42105             v = Date.parseDate(value, 'Y-m-d');
42106         }
42107         if (v) {
42108             // 
42109             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42110         }
42111         
42112         
42113         if(!v && this.altFormats){
42114             if(!this.altFormatsArray){
42115                 this.altFormatsArray = this.altFormats.split("|");
42116             }
42117             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42118                 v = Date.parseDate(value, this.altFormatsArray[i]);
42119             }
42120         }
42121         return v;
42122     },
42123
42124     // private
42125     formatDate : function(date, fmt){
42126         return (!date || !(date instanceof Date)) ?
42127                date : date.dateFormat(fmt || this.format);
42128     },
42129
42130     // private
42131     menuListeners : {
42132         select: function(m, d){
42133             this.setValue(d);
42134             this.fireEvent('select', this, d);
42135         },
42136         show : function(){ // retain focus styling
42137             this.onFocus();
42138         },
42139         hide : function(){
42140             this.focus.defer(10, this);
42141             var ml = this.menuListeners;
42142             this.menu.un("select", ml.select,  this);
42143             this.menu.un("show", ml.show,  this);
42144             this.menu.un("hide", ml.hide,  this);
42145         }
42146     },
42147     // private
42148     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42149     onTriggerClick : function(){
42150         if(this.disabled){
42151             return;
42152         }
42153         if(this.menu == null){
42154             this.menu = new Roo.menu.DateMenu();
42155            
42156         }
42157         
42158         Roo.apply(this.menu.picker,  {
42159             
42160             showClear: this.allowBlank,
42161             minDate : this.minValue,
42162             maxDate : this.maxValue,
42163             disabledDatesRE : this.ddMatch,
42164             disabledDatesText : this.disabledDatesText,
42165             
42166             format : this.useIso ? 'Y-m-d' : this.format,
42167             minText : String.format(this.minText, this.formatDate(this.minValue)),
42168             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42169             
42170         });
42171          this.menu.on(Roo.apply({}, this.menuListeners, {
42172             scope:this
42173         }));
42174        
42175         
42176         var m = this.menu;
42177         var p = m.picker;
42178         
42179         // hide month picker get's called when we called by 'before hide';
42180         
42181         var ignorehide = true;
42182         p.hideMonthPicker  = function(disableAnim){
42183             if (ignorehide) {
42184                 return;
42185             }
42186              if(this.monthPicker){
42187                 Roo.log("hideMonthPicker called");
42188                 if(disableAnim === true){
42189                     this.monthPicker.hide();
42190                 }else{
42191                     this.monthPicker.slideOut('t', {duration:.2});
42192                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42193                     p.fireEvent("select", this, this.value);
42194                     m.hide();
42195                 }
42196             }
42197         }
42198         
42199         Roo.log('picker set value');
42200         Roo.log(this.getValue());
42201         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42202         m.show(this.el, 'tl-bl?');
42203         ignorehide  = false;
42204         // this will trigger hideMonthPicker..
42205         
42206         
42207         // hidden the day picker
42208         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42209         
42210         
42211         
42212       
42213         
42214         p.showMonthPicker.defer(100, p);
42215     
42216         
42217        
42218     },
42219
42220     beforeBlur : function(){
42221         var v = this.parseDate(this.getRawValue());
42222         if(v){
42223             this.setValue(v);
42224         }
42225     }
42226
42227     /** @cfg {Boolean} grow @hide */
42228     /** @cfg {Number} growMin @hide */
42229     /** @cfg {Number} growMax @hide */
42230     /**
42231      * @hide
42232      * @method autoSize
42233      */
42234 });/*
42235  * Based on:
42236  * Ext JS Library 1.1.1
42237  * Copyright(c) 2006-2007, Ext JS, LLC.
42238  *
42239  * Originally Released Under LGPL - original licence link has changed is not relivant.
42240  *
42241  * Fork - LGPL
42242  * <script type="text/javascript">
42243  */
42244  
42245
42246 /**
42247  * @class Roo.form.ComboBox
42248  * @extends Roo.form.TriggerField
42249  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42250  * @constructor
42251  * Create a new ComboBox.
42252  * @param {Object} config Configuration options
42253  */
42254 Roo.form.ComboBox = function(config){
42255     Roo.form.ComboBox.superclass.constructor.call(this, config);
42256     this.addEvents({
42257         /**
42258          * @event expand
42259          * Fires when the dropdown list is expanded
42260              * @param {Roo.form.ComboBox} combo This combo box
42261              */
42262         'expand' : true,
42263         /**
42264          * @event collapse
42265          * Fires when the dropdown list is collapsed
42266              * @param {Roo.form.ComboBox} combo This combo box
42267              */
42268         'collapse' : true,
42269         /**
42270          * @event beforeselect
42271          * Fires before a list item is selected. Return false to cancel the selection.
42272              * @param {Roo.form.ComboBox} combo This combo box
42273              * @param {Roo.data.Record} record The data record returned from the underlying store
42274              * @param {Number} index The index of the selected item in the dropdown list
42275              */
42276         'beforeselect' : true,
42277         /**
42278          * @event select
42279          * Fires when a list item is selected
42280              * @param {Roo.form.ComboBox} combo This combo box
42281              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42282              * @param {Number} index The index of the selected item in the dropdown list
42283              */
42284         'select' : true,
42285         /**
42286          * @event beforequery
42287          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42288          * The event object passed has these properties:
42289              * @param {Roo.form.ComboBox} combo This combo box
42290              * @param {String} query The query
42291              * @param {Boolean} forceAll true to force "all" query
42292              * @param {Boolean} cancel true to cancel the query
42293              * @param {Object} e The query event object
42294              */
42295         'beforequery': true,
42296          /**
42297          * @event add
42298          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42299              * @param {Roo.form.ComboBox} combo This combo box
42300              */
42301         'add' : true,
42302         /**
42303          * @event edit
42304          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42305              * @param {Roo.form.ComboBox} combo This combo box
42306              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42307              */
42308         'edit' : true
42309         
42310         
42311     });
42312     if(this.transform){
42313         this.allowDomMove = false;
42314         var s = Roo.getDom(this.transform);
42315         if(!this.hiddenName){
42316             this.hiddenName = s.name;
42317         }
42318         if(!this.store){
42319             this.mode = 'local';
42320             var d = [], opts = s.options;
42321             for(var i = 0, len = opts.length;i < len; i++){
42322                 var o = opts[i];
42323                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42324                 if(o.selected) {
42325                     this.value = value;
42326                 }
42327                 d.push([value, o.text]);
42328             }
42329             this.store = new Roo.data.SimpleStore({
42330                 'id': 0,
42331                 fields: ['value', 'text'],
42332                 data : d
42333             });
42334             this.valueField = 'value';
42335             this.displayField = 'text';
42336         }
42337         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42338         if(!this.lazyRender){
42339             this.target = true;
42340             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42341             s.parentNode.removeChild(s); // remove it
42342             this.render(this.el.parentNode);
42343         }else{
42344             s.parentNode.removeChild(s); // remove it
42345         }
42346
42347     }
42348     if (this.store) {
42349         this.store = Roo.factory(this.store, Roo.data);
42350     }
42351     
42352     this.selectedIndex = -1;
42353     if(this.mode == 'local'){
42354         if(config.queryDelay === undefined){
42355             this.queryDelay = 10;
42356         }
42357         if(config.minChars === undefined){
42358             this.minChars = 0;
42359         }
42360     }
42361 };
42362
42363 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42364     /**
42365      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42366      */
42367     /**
42368      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42369      * rendering into an Roo.Editor, defaults to false)
42370      */
42371     /**
42372      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42373      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42374      */
42375     /**
42376      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42377      */
42378     /**
42379      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42380      * the dropdown list (defaults to undefined, with no header element)
42381      */
42382
42383      /**
42384      * @cfg {String/Roo.Template} tpl The template to use to render the output
42385      */
42386      
42387     // private
42388     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42389     /**
42390      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42391      */
42392     listWidth: undefined,
42393     /**
42394      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42395      * mode = 'remote' or 'text' if mode = 'local')
42396      */
42397     displayField: undefined,
42398     /**
42399      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42400      * mode = 'remote' or 'value' if mode = 'local'). 
42401      * Note: use of a valueField requires the user make a selection
42402      * in order for a value to be mapped.
42403      */
42404     valueField: undefined,
42405     
42406     
42407     /**
42408      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42409      * field's data value (defaults to the underlying DOM element's name)
42410      */
42411     hiddenName: undefined,
42412     /**
42413      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42414      */
42415     listClass: '',
42416     /**
42417      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42418      */
42419     selectedClass: 'x-combo-selected',
42420     /**
42421      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42422      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42423      * which displays a downward arrow icon).
42424      */
42425     triggerClass : 'x-form-arrow-trigger',
42426     /**
42427      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42428      */
42429     shadow:'sides',
42430     /**
42431      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42432      * anchor positions (defaults to 'tl-bl')
42433      */
42434     listAlign: 'tl-bl?',
42435     /**
42436      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42437      */
42438     maxHeight: 300,
42439     /**
42440      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42441      * query specified by the allQuery config option (defaults to 'query')
42442      */
42443     triggerAction: 'query',
42444     /**
42445      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42446      * (defaults to 4, does not apply if editable = false)
42447      */
42448     minChars : 4,
42449     /**
42450      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42451      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42452      */
42453     typeAhead: false,
42454     /**
42455      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42456      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42457      */
42458     queryDelay: 500,
42459     /**
42460      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42461      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42462      */
42463     pageSize: 0,
42464     /**
42465      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42466      * when editable = true (defaults to false)
42467      */
42468     selectOnFocus:false,
42469     /**
42470      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42471      */
42472     queryParam: 'query',
42473     /**
42474      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42475      * when mode = 'remote' (defaults to 'Loading...')
42476      */
42477     loadingText: 'Loading...',
42478     /**
42479      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42480      */
42481     resizable: false,
42482     /**
42483      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42484      */
42485     handleHeight : 8,
42486     /**
42487      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42488      * traditional select (defaults to true)
42489      */
42490     editable: true,
42491     /**
42492      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42493      */
42494     allQuery: '',
42495     /**
42496      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42497      */
42498     mode: 'remote',
42499     /**
42500      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42501      * listWidth has a higher value)
42502      */
42503     minListWidth : 70,
42504     /**
42505      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42506      * allow the user to set arbitrary text into the field (defaults to false)
42507      */
42508     forceSelection:false,
42509     /**
42510      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42511      * if typeAhead = true (defaults to 250)
42512      */
42513     typeAheadDelay : 250,
42514     /**
42515      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42516      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42517      */
42518     valueNotFoundText : undefined,
42519     /**
42520      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42521      */
42522     blockFocus : false,
42523     
42524     /**
42525      * @cfg {Boolean} disableClear Disable showing of clear button.
42526      */
42527     disableClear : false,
42528     /**
42529      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42530      */
42531     alwaysQuery : false,
42532     
42533     //private
42534     addicon : false,
42535     editicon: false,
42536     
42537     // element that contains real text value.. (when hidden is used..)
42538      
42539     // private
42540     onRender : function(ct, position)
42541     {
42542         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42543         
42544         if(this.hiddenName){
42545             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42546                     'before', true);
42547             this.hiddenField.value =
42548                 this.hiddenValue !== undefined ? this.hiddenValue :
42549                 this.value !== undefined ? this.value : '';
42550
42551             // prevent input submission
42552             this.el.dom.removeAttribute('name');
42553              
42554              
42555         }
42556         
42557         if(Roo.isGecko){
42558             this.el.dom.setAttribute('autocomplete', 'off');
42559         }
42560
42561         var cls = 'x-combo-list';
42562
42563         this.list = new Roo.Layer({
42564             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42565         });
42566
42567         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42568         this.list.setWidth(lw);
42569         this.list.swallowEvent('mousewheel');
42570         this.assetHeight = 0;
42571
42572         if(this.title){
42573             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42574             this.assetHeight += this.header.getHeight();
42575         }
42576
42577         this.innerList = this.list.createChild({cls:cls+'-inner'});
42578         this.innerList.on('mouseover', this.onViewOver, this);
42579         this.innerList.on('mousemove', this.onViewMove, this);
42580         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42581         
42582         if(this.allowBlank && !this.pageSize && !this.disableClear){
42583             this.footer = this.list.createChild({cls:cls+'-ft'});
42584             this.pageTb = new Roo.Toolbar(this.footer);
42585            
42586         }
42587         if(this.pageSize){
42588             this.footer = this.list.createChild({cls:cls+'-ft'});
42589             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42590                     {pageSize: this.pageSize});
42591             
42592         }
42593         
42594         if (this.pageTb && this.allowBlank && !this.disableClear) {
42595             var _this = this;
42596             this.pageTb.add(new Roo.Toolbar.Fill(), {
42597                 cls: 'x-btn-icon x-btn-clear',
42598                 text: '&#160;',
42599                 handler: function()
42600                 {
42601                     _this.collapse();
42602                     _this.clearValue();
42603                     _this.onSelect(false, -1);
42604                 }
42605             });
42606         }
42607         if (this.footer) {
42608             this.assetHeight += this.footer.getHeight();
42609         }
42610         
42611
42612         if(!this.tpl){
42613             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42614         }
42615
42616         this.view = new Roo.View(this.innerList, this.tpl, {
42617             singleSelect:true,
42618             store: this.store,
42619             selectedClass: this.selectedClass
42620         });
42621
42622         this.view.on('click', this.onViewClick, this);
42623
42624         this.store.on('beforeload', this.onBeforeLoad, this);
42625         this.store.on('load', this.onLoad, this);
42626         this.store.on('loadexception', this.onLoadException, this);
42627
42628         if(this.resizable){
42629             this.resizer = new Roo.Resizable(this.list,  {
42630                pinned:true, handles:'se'
42631             });
42632             this.resizer.on('resize', function(r, w, h){
42633                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42634                 this.listWidth = w;
42635                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42636                 this.restrictHeight();
42637             }, this);
42638             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42639         }
42640         if(!this.editable){
42641             this.editable = true;
42642             this.setEditable(false);
42643         }  
42644         
42645         
42646         if (typeof(this.events.add.listeners) != 'undefined') {
42647             
42648             this.addicon = this.wrap.createChild(
42649                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42650        
42651             this.addicon.on('click', function(e) {
42652                 this.fireEvent('add', this);
42653             }, this);
42654         }
42655         if (typeof(this.events.edit.listeners) != 'undefined') {
42656             
42657             this.editicon = this.wrap.createChild(
42658                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42659             if (this.addicon) {
42660                 this.editicon.setStyle('margin-left', '40px');
42661             }
42662             this.editicon.on('click', function(e) {
42663                 
42664                 // we fire even  if inothing is selected..
42665                 this.fireEvent('edit', this, this.lastData );
42666                 
42667             }, this);
42668         }
42669         
42670         
42671         
42672     },
42673
42674     // private
42675     initEvents : function(){
42676         Roo.form.ComboBox.superclass.initEvents.call(this);
42677
42678         this.keyNav = new Roo.KeyNav(this.el, {
42679             "up" : function(e){
42680                 this.inKeyMode = true;
42681                 this.selectPrev();
42682             },
42683
42684             "down" : function(e){
42685                 if(!this.isExpanded()){
42686                     this.onTriggerClick();
42687                 }else{
42688                     this.inKeyMode = true;
42689                     this.selectNext();
42690                 }
42691             },
42692
42693             "enter" : function(e){
42694                 this.onViewClick();
42695                 //return true;
42696             },
42697
42698             "esc" : function(e){
42699                 this.collapse();
42700             },
42701
42702             "tab" : function(e){
42703                 this.onViewClick(false);
42704                 this.fireEvent("specialkey", this, e);
42705                 return true;
42706             },
42707
42708             scope : this,
42709
42710             doRelay : function(foo, bar, hname){
42711                 if(hname == 'down' || this.scope.isExpanded()){
42712                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42713                 }
42714                 return true;
42715             },
42716
42717             forceKeyDown: true
42718         });
42719         this.queryDelay = Math.max(this.queryDelay || 10,
42720                 this.mode == 'local' ? 10 : 250);
42721         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42722         if(this.typeAhead){
42723             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42724         }
42725         if(this.editable !== false){
42726             this.el.on("keyup", this.onKeyUp, this);
42727         }
42728         if(this.forceSelection){
42729             this.on('blur', this.doForce, this);
42730         }
42731     },
42732
42733     onDestroy : function(){
42734         if(this.view){
42735             this.view.setStore(null);
42736             this.view.el.removeAllListeners();
42737             this.view.el.remove();
42738             this.view.purgeListeners();
42739         }
42740         if(this.list){
42741             this.list.destroy();
42742         }
42743         if(this.store){
42744             this.store.un('beforeload', this.onBeforeLoad, this);
42745             this.store.un('load', this.onLoad, this);
42746             this.store.un('loadexception', this.onLoadException, this);
42747         }
42748         Roo.form.ComboBox.superclass.onDestroy.call(this);
42749     },
42750
42751     // private
42752     fireKey : function(e){
42753         if(e.isNavKeyPress() && !this.list.isVisible()){
42754             this.fireEvent("specialkey", this, e);
42755         }
42756     },
42757
42758     // private
42759     onResize: function(w, h){
42760         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42761         
42762         if(typeof w != 'number'){
42763             // we do not handle it!?!?
42764             return;
42765         }
42766         var tw = this.trigger.getWidth();
42767         tw += this.addicon ? this.addicon.getWidth() : 0;
42768         tw += this.editicon ? this.editicon.getWidth() : 0;
42769         var x = w - tw;
42770         this.el.setWidth( this.adjustWidth('input', x));
42771             
42772         this.trigger.setStyle('left', x+'px');
42773         
42774         if(this.list && this.listWidth === undefined){
42775             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42776             this.list.setWidth(lw);
42777             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42778         }
42779         
42780     
42781         
42782     },
42783
42784     /**
42785      * Allow or prevent the user from directly editing the field text.  If false is passed,
42786      * the user will only be able to select from the items defined in the dropdown list.  This method
42787      * is the runtime equivalent of setting the 'editable' config option at config time.
42788      * @param {Boolean} value True to allow the user to directly edit the field text
42789      */
42790     setEditable : function(value){
42791         if(value == this.editable){
42792             return;
42793         }
42794         this.editable = value;
42795         if(!value){
42796             this.el.dom.setAttribute('readOnly', true);
42797             this.el.on('mousedown', this.onTriggerClick,  this);
42798             this.el.addClass('x-combo-noedit');
42799         }else{
42800             this.el.dom.setAttribute('readOnly', false);
42801             this.el.un('mousedown', this.onTriggerClick,  this);
42802             this.el.removeClass('x-combo-noedit');
42803         }
42804     },
42805
42806     // private
42807     onBeforeLoad : function(){
42808         if(!this.hasFocus){
42809             return;
42810         }
42811         this.innerList.update(this.loadingText ?
42812                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42813         this.restrictHeight();
42814         this.selectedIndex = -1;
42815     },
42816
42817     // private
42818     onLoad : function(){
42819         if(!this.hasFocus){
42820             return;
42821         }
42822         if(this.store.getCount() > 0){
42823             this.expand();
42824             this.restrictHeight();
42825             if(this.lastQuery == this.allQuery){
42826                 if(this.editable){
42827                     this.el.dom.select();
42828                 }
42829                 if(!this.selectByValue(this.value, true)){
42830                     this.select(0, true);
42831                 }
42832             }else{
42833                 this.selectNext();
42834                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42835                     this.taTask.delay(this.typeAheadDelay);
42836                 }
42837             }
42838         }else{
42839             this.onEmptyResults();
42840         }
42841         //this.el.focus();
42842     },
42843     // private
42844     onLoadException : function()
42845     {
42846         this.collapse();
42847         Roo.log(this.store.reader.jsonData);
42848         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42849             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42850         }
42851         
42852         
42853     },
42854     // private
42855     onTypeAhead : function(){
42856         if(this.store.getCount() > 0){
42857             var r = this.store.getAt(0);
42858             var newValue = r.data[this.displayField];
42859             var len = newValue.length;
42860             var selStart = this.getRawValue().length;
42861             if(selStart != len){
42862                 this.setRawValue(newValue);
42863                 this.selectText(selStart, newValue.length);
42864             }
42865         }
42866     },
42867
42868     // private
42869     onSelect : function(record, index){
42870         if(this.fireEvent('beforeselect', this, record, index) !== false){
42871             this.setFromData(index > -1 ? record.data : false);
42872             this.collapse();
42873             this.fireEvent('select', this, record, index);
42874         }
42875     },
42876
42877     /**
42878      * Returns the currently selected field value or empty string if no value is set.
42879      * @return {String} value The selected value
42880      */
42881     getValue : function(){
42882         if(this.valueField){
42883             return typeof this.value != 'undefined' ? this.value : '';
42884         }
42885         return Roo.form.ComboBox.superclass.getValue.call(this);
42886     },
42887
42888     /**
42889      * Clears any text/value currently set in the field
42890      */
42891     clearValue : function(){
42892         if(this.hiddenField){
42893             this.hiddenField.value = '';
42894         }
42895         this.value = '';
42896         this.setRawValue('');
42897         this.lastSelectionText = '';
42898         
42899     },
42900
42901     /**
42902      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42903      * will be displayed in the field.  If the value does not match the data value of an existing item,
42904      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42905      * Otherwise the field will be blank (although the value will still be set).
42906      * @param {String} value The value to match
42907      */
42908     setValue : function(v){
42909         var text = v;
42910         if(this.valueField){
42911             var r = this.findRecord(this.valueField, v);
42912             if(r){
42913                 text = r.data[this.displayField];
42914             }else if(this.valueNotFoundText !== undefined){
42915                 text = this.valueNotFoundText;
42916             }
42917         }
42918         this.lastSelectionText = text;
42919         if(this.hiddenField){
42920             this.hiddenField.value = v;
42921         }
42922         Roo.form.ComboBox.superclass.setValue.call(this, text);
42923         this.value = v;
42924     },
42925     /**
42926      * @property {Object} the last set data for the element
42927      */
42928     
42929     lastData : false,
42930     /**
42931      * Sets the value of the field based on a object which is related to the record format for the store.
42932      * @param {Object} value the value to set as. or false on reset?
42933      */
42934     setFromData : function(o){
42935         var dv = ''; // display value
42936         var vv = ''; // value value..
42937         this.lastData = o;
42938         if (this.displayField) {
42939             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42940         } else {
42941             // this is an error condition!!!
42942             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42943         }
42944         
42945         if(this.valueField){
42946             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42947         }
42948         if(this.hiddenField){
42949             this.hiddenField.value = vv;
42950             
42951             this.lastSelectionText = dv;
42952             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42953             this.value = vv;
42954             return;
42955         }
42956         // no hidden field.. - we store the value in 'value', but still display
42957         // display field!!!!
42958         this.lastSelectionText = dv;
42959         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42960         this.value = vv;
42961         
42962         
42963     },
42964     // private
42965     reset : function(){
42966         // overridden so that last data is reset..
42967         this.setValue(this.resetValue);
42968         this.originalValue = this.getValue();
42969         this.clearInvalid();
42970         this.lastData = false;
42971         if (this.view) {
42972             this.view.clearSelections();
42973         }
42974     },
42975     // private
42976     findRecord : function(prop, value){
42977         var record;
42978         if(this.store.getCount() > 0){
42979             this.store.each(function(r){
42980                 if(r.data[prop] == value){
42981                     record = r;
42982                     return false;
42983                 }
42984                 return true;
42985             });
42986         }
42987         return record;
42988     },
42989     
42990     getName: function()
42991     {
42992         // returns hidden if it's set..
42993         if (!this.rendered) {return ''};
42994         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42995         
42996     },
42997     // private
42998     onViewMove : function(e, t){
42999         this.inKeyMode = false;
43000     },
43001
43002     // private
43003     onViewOver : function(e, t){
43004         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43005             return;
43006         }
43007         var item = this.view.findItemFromChild(t);
43008         if(item){
43009             var index = this.view.indexOf(item);
43010             this.select(index, false);
43011         }
43012     },
43013
43014     // private
43015     onViewClick : function(doFocus)
43016     {
43017         var index = this.view.getSelectedIndexes()[0];
43018         var r = this.store.getAt(index);
43019         if(r){
43020             this.onSelect(r, index);
43021         }
43022         if(doFocus !== false && !this.blockFocus){
43023             this.el.focus();
43024         }
43025     },
43026
43027     // private
43028     restrictHeight : function(){
43029         this.innerList.dom.style.height = '';
43030         var inner = this.innerList.dom;
43031         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43032         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43033         this.list.beginUpdate();
43034         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43035         this.list.alignTo(this.el, this.listAlign);
43036         this.list.endUpdate();
43037     },
43038
43039     // private
43040     onEmptyResults : function(){
43041         this.collapse();
43042     },
43043
43044     /**
43045      * Returns true if the dropdown list is expanded, else false.
43046      */
43047     isExpanded : function(){
43048         return this.list.isVisible();
43049     },
43050
43051     /**
43052      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43053      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43054      * @param {String} value The data value of the item to select
43055      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43056      * selected item if it is not currently in view (defaults to true)
43057      * @return {Boolean} True if the value matched an item in the list, else false
43058      */
43059     selectByValue : function(v, scrollIntoView){
43060         if(v !== undefined && v !== null){
43061             var r = this.findRecord(this.valueField || this.displayField, v);
43062             if(r){
43063                 this.select(this.store.indexOf(r), scrollIntoView);
43064                 return true;
43065             }
43066         }
43067         return false;
43068     },
43069
43070     /**
43071      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43072      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43073      * @param {Number} index The zero-based index of the list item to select
43074      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43075      * selected item if it is not currently in view (defaults to true)
43076      */
43077     select : function(index, scrollIntoView){
43078         this.selectedIndex = index;
43079         this.view.select(index);
43080         if(scrollIntoView !== false){
43081             var el = this.view.getNode(index);
43082             if(el){
43083                 this.innerList.scrollChildIntoView(el, false);
43084             }
43085         }
43086     },
43087
43088     // private
43089     selectNext : function(){
43090         var ct = this.store.getCount();
43091         if(ct > 0){
43092             if(this.selectedIndex == -1){
43093                 this.select(0);
43094             }else if(this.selectedIndex < ct-1){
43095                 this.select(this.selectedIndex+1);
43096             }
43097         }
43098     },
43099
43100     // private
43101     selectPrev : function(){
43102         var ct = this.store.getCount();
43103         if(ct > 0){
43104             if(this.selectedIndex == -1){
43105                 this.select(0);
43106             }else if(this.selectedIndex != 0){
43107                 this.select(this.selectedIndex-1);
43108             }
43109         }
43110     },
43111
43112     // private
43113     onKeyUp : function(e){
43114         if(this.editable !== false && !e.isSpecialKey()){
43115             this.lastKey = e.getKey();
43116             this.dqTask.delay(this.queryDelay);
43117         }
43118     },
43119
43120     // private
43121     validateBlur : function(){
43122         return !this.list || !this.list.isVisible();   
43123     },
43124
43125     // private
43126     initQuery : function(){
43127         this.doQuery(this.getRawValue());
43128     },
43129
43130     // private
43131     doForce : function(){
43132         if(this.el.dom.value.length > 0){
43133             this.el.dom.value =
43134                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43135              
43136         }
43137     },
43138
43139     /**
43140      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43141      * query allowing the query action to be canceled if needed.
43142      * @param {String} query The SQL query to execute
43143      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43144      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43145      * saved in the current store (defaults to false)
43146      */
43147     doQuery : function(q, forceAll){
43148         if(q === undefined || q === null){
43149             q = '';
43150         }
43151         var qe = {
43152             query: q,
43153             forceAll: forceAll,
43154             combo: this,
43155             cancel:false
43156         };
43157         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43158             return false;
43159         }
43160         q = qe.query;
43161         forceAll = qe.forceAll;
43162         if(forceAll === true || (q.length >= this.minChars)){
43163             if(this.lastQuery != q || this.alwaysQuery){
43164                 this.lastQuery = q;
43165                 if(this.mode == 'local'){
43166                     this.selectedIndex = -1;
43167                     if(forceAll){
43168                         this.store.clearFilter();
43169                     }else{
43170                         this.store.filter(this.displayField, q);
43171                     }
43172                     this.onLoad();
43173                 }else{
43174                     this.store.baseParams[this.queryParam] = q;
43175                     this.store.load({
43176                         params: this.getParams(q)
43177                     });
43178                     this.expand();
43179                 }
43180             }else{
43181                 this.selectedIndex = -1;
43182                 this.onLoad();   
43183             }
43184         }
43185     },
43186
43187     // private
43188     getParams : function(q){
43189         var p = {};
43190         //p[this.queryParam] = q;
43191         if(this.pageSize){
43192             p.start = 0;
43193             p.limit = this.pageSize;
43194         }
43195         return p;
43196     },
43197
43198     /**
43199      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43200      */
43201     collapse : function(){
43202         if(!this.isExpanded()){
43203             return;
43204         }
43205         this.list.hide();
43206         Roo.get(document).un('mousedown', this.collapseIf, this);
43207         Roo.get(document).un('mousewheel', this.collapseIf, this);
43208         if (!this.editable) {
43209             Roo.get(document).un('keydown', this.listKeyPress, this);
43210         }
43211         this.fireEvent('collapse', this);
43212     },
43213
43214     // private
43215     collapseIf : function(e){
43216         if(!e.within(this.wrap) && !e.within(this.list)){
43217             this.collapse();
43218         }
43219     },
43220
43221     /**
43222      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43223      */
43224     expand : function(){
43225         if(this.isExpanded() || !this.hasFocus){
43226             return;
43227         }
43228         this.list.alignTo(this.el, this.listAlign);
43229         this.list.show();
43230         Roo.get(document).on('mousedown', this.collapseIf, this);
43231         Roo.get(document).on('mousewheel', this.collapseIf, this);
43232         if (!this.editable) {
43233             Roo.get(document).on('keydown', this.listKeyPress, this);
43234         }
43235         
43236         this.fireEvent('expand', this);
43237     },
43238
43239     // private
43240     // Implements the default empty TriggerField.onTriggerClick function
43241     onTriggerClick : function(){
43242         if(this.disabled){
43243             return;
43244         }
43245         if(this.isExpanded()){
43246             this.collapse();
43247             if (!this.blockFocus) {
43248                 this.el.focus();
43249             }
43250             
43251         }else {
43252             this.hasFocus = true;
43253             if(this.triggerAction == 'all') {
43254                 this.doQuery(this.allQuery, true);
43255             } else {
43256                 this.doQuery(this.getRawValue());
43257             }
43258             if (!this.blockFocus) {
43259                 this.el.focus();
43260             }
43261         }
43262     },
43263     listKeyPress : function(e)
43264     {
43265         //Roo.log('listkeypress');
43266         // scroll to first matching element based on key pres..
43267         if (e.isSpecialKey()) {
43268             return false;
43269         }
43270         var k = String.fromCharCode(e.getKey()).toUpperCase();
43271         //Roo.log(k);
43272         var match  = false;
43273         var csel = this.view.getSelectedNodes();
43274         var cselitem = false;
43275         if (csel.length) {
43276             var ix = this.view.indexOf(csel[0]);
43277             cselitem  = this.store.getAt(ix);
43278             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43279                 cselitem = false;
43280             }
43281             
43282         }
43283         
43284         this.store.each(function(v) { 
43285             if (cselitem) {
43286                 // start at existing selection.
43287                 if (cselitem.id == v.id) {
43288                     cselitem = false;
43289                 }
43290                 return;
43291             }
43292                 
43293             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43294                 match = this.store.indexOf(v);
43295                 return false;
43296             }
43297         }, this);
43298         
43299         if (match === false) {
43300             return true; // no more action?
43301         }
43302         // scroll to?
43303         this.view.select(match);
43304         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43305         sn.scrollIntoView(sn.dom.parentNode, false);
43306     } 
43307
43308     /** 
43309     * @cfg {Boolean} grow 
43310     * @hide 
43311     */
43312     /** 
43313     * @cfg {Number} growMin 
43314     * @hide 
43315     */
43316     /** 
43317     * @cfg {Number} growMax 
43318     * @hide 
43319     */
43320     /**
43321      * @hide
43322      * @method autoSize
43323      */
43324 });/*
43325  * Copyright(c) 2010-2012, Roo J Solutions Limited
43326  *
43327  * Licence LGPL
43328  *
43329  */
43330
43331 /**
43332  * @class Roo.form.ComboBoxArray
43333  * @extends Roo.form.TextField
43334  * A facebook style adder... for lists of email / people / countries  etc...
43335  * pick multiple items from a combo box, and shows each one.
43336  *
43337  *  Fred [x]  Brian [x]  [Pick another |v]
43338  *
43339  *
43340  *  For this to work: it needs various extra information
43341  *    - normal combo problay has
43342  *      name, hiddenName
43343  *    + displayField, valueField
43344  *
43345  *    For our purpose...
43346  *
43347  *
43348  *   If we change from 'extends' to wrapping...
43349  *   
43350  *  
43351  *
43352  
43353  
43354  * @constructor
43355  * Create a new ComboBoxArray.
43356  * @param {Object} config Configuration options
43357  */
43358  
43359
43360 Roo.form.ComboBoxArray = function(config)
43361 {
43362     this.addEvents({
43363         /**
43364          * @event beforeremove
43365          * Fires before remove the value from the list
43366              * @param {Roo.form.ComboBoxArray} _self This combo box array
43367              * @param {Roo.form.ComboBoxArray.Item} item removed item
43368              */
43369         'beforeremove' : true,
43370         /**
43371          * @event remove
43372          * Fires when remove the value from the list
43373              * @param {Roo.form.ComboBoxArray} _self This combo box array
43374              * @param {Roo.form.ComboBoxArray.Item} item removed item
43375              */
43376         'remove' : true
43377         
43378         
43379     });
43380     
43381     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43382     
43383     this.items = new Roo.util.MixedCollection(false);
43384     
43385     // construct the child combo...
43386     
43387     
43388     
43389     
43390    
43391     
43392 }
43393
43394  
43395 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43396
43397     /**
43398      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43399      */
43400     
43401     lastData : false,
43402     
43403     // behavies liek a hiddne field
43404     inputType:      'hidden',
43405     /**
43406      * @cfg {Number} width The width of the box that displays the selected element
43407      */ 
43408     width:          300,
43409
43410     
43411     
43412     /**
43413      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43414      */
43415     name : false,
43416     /**
43417      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43418      */
43419     hiddenName : false,
43420       /**
43421      * @cfg {String} seperator    The value seperator normally ',' 
43422      */
43423     seperator : ',',
43424     
43425     // private the array of items that are displayed..
43426     items  : false,
43427     // private - the hidden field el.
43428     hiddenEl : false,
43429     // private - the filed el..
43430     el : false,
43431     
43432     //validateValue : function() { return true; }, // all values are ok!
43433     //onAddClick: function() { },
43434     
43435     onRender : function(ct, position) 
43436     {
43437         
43438         // create the standard hidden element
43439         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43440         
43441         
43442         // give fake names to child combo;
43443         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43444         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43445         
43446         this.combo = Roo.factory(this.combo, Roo.form);
43447         this.combo.onRender(ct, position);
43448         if (typeof(this.combo.width) != 'undefined') {
43449             this.combo.onResize(this.combo.width,0);
43450         }
43451         
43452         this.combo.initEvents();
43453         
43454         // assigned so form know we need to do this..
43455         this.store          = this.combo.store;
43456         this.valueField     = this.combo.valueField;
43457         this.displayField   = this.combo.displayField ;
43458         
43459         
43460         this.combo.wrap.addClass('x-cbarray-grp');
43461         
43462         var cbwrap = this.combo.wrap.createChild(
43463             {tag: 'div', cls: 'x-cbarray-cb'},
43464             this.combo.el.dom
43465         );
43466         
43467              
43468         this.hiddenEl = this.combo.wrap.createChild({
43469             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43470         });
43471         this.el = this.combo.wrap.createChild({
43472             tag: 'input',  type:'hidden' , name: this.name, value : ''
43473         });
43474          //   this.el.dom.removeAttribute("name");
43475         
43476         
43477         this.outerWrap = this.combo.wrap;
43478         this.wrap = cbwrap;
43479         
43480         this.outerWrap.setWidth(this.width);
43481         this.outerWrap.dom.removeChild(this.el.dom);
43482         
43483         this.wrap.dom.appendChild(this.el.dom);
43484         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43485         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43486         
43487         this.combo.trigger.setStyle('position','relative');
43488         this.combo.trigger.setStyle('left', '0px');
43489         this.combo.trigger.setStyle('top', '2px');
43490         
43491         this.combo.el.setStyle('vertical-align', 'text-bottom');
43492         
43493         //this.trigger.setStyle('vertical-align', 'top');
43494         
43495         // this should use the code from combo really... on('add' ....)
43496         if (this.adder) {
43497             
43498         
43499             this.adder = this.outerWrap.createChild(
43500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43501             var _t = this;
43502             this.adder.on('click', function(e) {
43503                 _t.fireEvent('adderclick', this, e);
43504             }, _t);
43505         }
43506         //var _t = this;
43507         //this.adder.on('click', this.onAddClick, _t);
43508         
43509         
43510         this.combo.on('select', function(cb, rec, ix) {
43511             this.addItem(rec.data);
43512             
43513             cb.setValue('');
43514             cb.el.dom.value = '';
43515             //cb.lastData = rec.data;
43516             // add to list
43517             
43518         }, this);
43519         
43520         
43521     },
43522     
43523     
43524     getName: function()
43525     {
43526         // returns hidden if it's set..
43527         if (!this.rendered) {return ''};
43528         return  this.hiddenName ? this.hiddenName : this.name;
43529         
43530     },
43531     
43532     
43533     onResize: function(w, h){
43534         
43535         return;
43536         // not sure if this is needed..
43537         //this.combo.onResize(w,h);
43538         
43539         if(typeof w != 'number'){
43540             // we do not handle it!?!?
43541             return;
43542         }
43543         var tw = this.combo.trigger.getWidth();
43544         tw += this.addicon ? this.addicon.getWidth() : 0;
43545         tw += this.editicon ? this.editicon.getWidth() : 0;
43546         var x = w - tw;
43547         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43548             
43549         this.combo.trigger.setStyle('left', '0px');
43550         
43551         if(this.list && this.listWidth === undefined){
43552             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43553             this.list.setWidth(lw);
43554             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43555         }
43556         
43557     
43558         
43559     },
43560     
43561     addItem: function(rec)
43562     {
43563         var valueField = this.combo.valueField;
43564         var displayField = this.combo.displayField;
43565         
43566         if (this.items.indexOfKey(rec[valueField]) > -1) {
43567             //console.log("GOT " + rec.data.id);
43568             return;
43569         }
43570         
43571         var x = new Roo.form.ComboBoxArray.Item({
43572             //id : rec[this.idField],
43573             data : rec,
43574             displayField : displayField ,
43575             tipField : displayField ,
43576             cb : this
43577         });
43578         // use the 
43579         this.items.add(rec[valueField],x);
43580         // add it before the element..
43581         this.updateHiddenEl();
43582         x.render(this.outerWrap, this.wrap.dom);
43583         // add the image handler..
43584     },
43585     
43586     updateHiddenEl : function()
43587     {
43588         this.validate();
43589         if (!this.hiddenEl) {
43590             return;
43591         }
43592         var ar = [];
43593         var idField = this.combo.valueField;
43594         
43595         this.items.each(function(f) {
43596             ar.push(f.data[idField]);
43597         });
43598         this.hiddenEl.dom.value = ar.join(this.seperator);
43599         this.validate();
43600     },
43601     
43602     reset : function()
43603     {
43604         this.items.clear();
43605         
43606         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43607            el.remove();
43608         });
43609         
43610         this.el.dom.value = '';
43611         if (this.hiddenEl) {
43612             this.hiddenEl.dom.value = '';
43613         }
43614         
43615     },
43616     getValue: function()
43617     {
43618         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43619     },
43620     setValue: function(v) // not a valid action - must use addItems..
43621     {
43622         
43623         this.reset();
43624          
43625         if (this.store.isLocal && (typeof(v) == 'string')) {
43626             // then we can use the store to find the values..
43627             // comma seperated at present.. this needs to allow JSON based encoding..
43628             this.hiddenEl.value  = v;
43629             var v_ar = [];
43630             Roo.each(v.split(this.seperator), function(k) {
43631                 Roo.log("CHECK " + this.valueField + ',' + k);
43632                 var li = this.store.query(this.valueField, k);
43633                 if (!li.length) {
43634                     return;
43635                 }
43636                 var add = {};
43637                 add[this.valueField] = k;
43638                 add[this.displayField] = li.item(0).data[this.displayField];
43639                 
43640                 this.addItem(add);
43641             }, this) 
43642              
43643         }
43644         if (typeof(v) == 'object' ) {
43645             // then let's assume it's an array of objects..
43646             Roo.each(v, function(l) {
43647                 var add = l;
43648                 if (typeof(l) == 'string') {
43649                     add = {};
43650                     add[this.valueField] = l;
43651                     add[this.displayField] = l
43652                 }
43653                 this.addItem(add);
43654             }, this);
43655              
43656         }
43657         
43658         
43659     },
43660     setFromData: function(v)
43661     {
43662         // this recieves an object, if setValues is called.
43663         this.reset();
43664         this.el.dom.value = v[this.displayField];
43665         this.hiddenEl.dom.value = v[this.valueField];
43666         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43667             return;
43668         }
43669         var kv = v[this.valueField];
43670         var dv = v[this.displayField];
43671         kv = typeof(kv) != 'string' ? '' : kv;
43672         dv = typeof(dv) != 'string' ? '' : dv;
43673         
43674         
43675         var keys = kv.split(this.seperator);
43676         var display = dv.split(this.seperator);
43677         for (var i = 0 ; i < keys.length; i++) {
43678             add = {};
43679             add[this.valueField] = keys[i];
43680             add[this.displayField] = display[i];
43681             this.addItem(add);
43682         }
43683       
43684         
43685     },
43686     
43687     /**
43688      * Validates the combox array value
43689      * @return {Boolean} True if the value is valid, else false
43690      */
43691     validate : function(){
43692         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43693             this.clearInvalid();
43694             return true;
43695         }
43696         return false;
43697     },
43698     
43699     validateValue : function(value){
43700         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43701         
43702     },
43703     
43704     /*@
43705      * overide
43706      * 
43707      */
43708     isDirty : function() {
43709         if(this.disabled) {
43710             return false;
43711         }
43712         
43713         try {
43714             var d = Roo.decode(String(this.originalValue));
43715         } catch (e) {
43716             return String(this.getValue()) !== String(this.originalValue);
43717         }
43718         
43719         var originalValue = [];
43720         
43721         for (var i = 0; i < d.length; i++){
43722             originalValue.push(d[i][this.valueField]);
43723         }
43724         
43725         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43726         
43727     }
43728     
43729 });
43730
43731
43732
43733 /**
43734  * @class Roo.form.ComboBoxArray.Item
43735  * @extends Roo.BoxComponent
43736  * A selected item in the list
43737  *  Fred [x]  Brian [x]  [Pick another |v]
43738  * 
43739  * @constructor
43740  * Create a new item.
43741  * @param {Object} config Configuration options
43742  */
43743  
43744 Roo.form.ComboBoxArray.Item = function(config) {
43745     config.id = Roo.id();
43746     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43747 }
43748
43749 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43750     data : {},
43751     cb: false,
43752     displayField : false,
43753     tipField : false,
43754     
43755     
43756     defaultAutoCreate : {
43757         tag: 'div',
43758         cls: 'x-cbarray-item',
43759         cn : [ 
43760             { tag: 'div' },
43761             {
43762                 tag: 'img',
43763                 width:16,
43764                 height : 16,
43765                 src : Roo.BLANK_IMAGE_URL ,
43766                 align: 'center'
43767             }
43768         ]
43769         
43770     },
43771     
43772  
43773     onRender : function(ct, position)
43774     {
43775         Roo.form.Field.superclass.onRender.call(this, ct, position);
43776         
43777         if(!this.el){
43778             var cfg = this.getAutoCreate();
43779             this.el = ct.createChild(cfg, position);
43780         }
43781         
43782         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43783         
43784         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43785             this.cb.renderer(this.data) :
43786             String.format('{0}',this.data[this.displayField]);
43787         
43788             
43789         this.el.child('div').dom.setAttribute('qtip',
43790                         String.format('{0}',this.data[this.tipField])
43791         );
43792         
43793         this.el.child('img').on('click', this.remove, this);
43794         
43795     },
43796    
43797     remove : function()
43798     {
43799         if(this.cb.disabled){
43800             return;
43801         }
43802         
43803         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43804             this.cb.items.remove(this);
43805             this.el.child('img').un('click', this.remove, this);
43806             this.el.remove();
43807             this.cb.updateHiddenEl();
43808
43809             this.cb.fireEvent('remove', this.cb, this);
43810         }
43811         
43812     }
43813 });/*
43814  * RooJS Library 1.1.1
43815  * Copyright(c) 2008-2011  Alan Knowles
43816  *
43817  * License - LGPL
43818  */
43819  
43820
43821 /**
43822  * @class Roo.form.ComboNested
43823  * @extends Roo.form.ComboBox
43824  * A combobox for that allows selection of nested items in a list,
43825  * eg.
43826  *
43827  *  Book
43828  *    -> red
43829  *    -> green
43830  *  Table
43831  *    -> square
43832  *      ->red
43833  *      ->green
43834  *    -> rectangle
43835  *      ->green
43836  *      
43837  * 
43838  * @constructor
43839  * Create a new ComboNested
43840  * @param {Object} config Configuration options
43841  */
43842 Roo.form.ComboNested = function(config){
43843     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43844     // should verify some data...
43845     // like
43846     // hiddenName = required..
43847     // displayField = required
43848     // valudField == required
43849     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43850     var _t = this;
43851     Roo.each(req, function(e) {
43852         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43853             throw "Roo.form.ComboNested : missing value for: " + e;
43854         }
43855     });
43856      
43857     
43858 };
43859
43860 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43861    
43862     /*
43863      * @config {Number} max Number of columns to show
43864      */
43865     
43866     maxColumns : 3,
43867    
43868     list : null, // the outermost div..
43869     innerLists : null, // the
43870     views : null,
43871     stores : null,
43872     // private
43873     loadingChildren : false,
43874     
43875     onRender : function(ct, position)
43876     {
43877         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43878         
43879         if(this.hiddenName){
43880             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43881                     'before', true);
43882             this.hiddenField.value =
43883                 this.hiddenValue !== undefined ? this.hiddenValue :
43884                 this.value !== undefined ? this.value : '';
43885
43886             // prevent input submission
43887             this.el.dom.removeAttribute('name');
43888              
43889              
43890         }
43891         
43892         if(Roo.isGecko){
43893             this.el.dom.setAttribute('autocomplete', 'off');
43894         }
43895
43896         var cls = 'x-combo-list';
43897
43898         this.list = new Roo.Layer({
43899             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43900         });
43901
43902         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43903         this.list.setWidth(lw);
43904         this.list.swallowEvent('mousewheel');
43905         this.assetHeight = 0;
43906
43907         if(this.title){
43908             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43909             this.assetHeight += this.header.getHeight();
43910         }
43911         this.innerLists = [];
43912         this.views = [];
43913         this.stores = [];
43914         for (var i =0 ; i < this.maxColumns; i++) {
43915             this.onRenderList( cls, i);
43916         }
43917         
43918         // always needs footer, as we are going to have an 'OK' button.
43919         this.footer = this.list.createChild({cls:cls+'-ft'});
43920         this.pageTb = new Roo.Toolbar(this.footer);  
43921         var _this = this;
43922         this.pageTb.add(  {
43923             
43924             text: 'Done',
43925             handler: function()
43926             {
43927                 _this.collapse();
43928             }
43929         });
43930         
43931         if ( this.allowBlank && !this.disableClear) {
43932             
43933             this.pageTb.add(new Roo.Toolbar.Fill(), {
43934                 cls: 'x-btn-icon x-btn-clear',
43935                 text: '&#160;',
43936                 handler: function()
43937                 {
43938                     _this.collapse();
43939                     _this.clearValue();
43940                     _this.onSelect(false, -1);
43941                 }
43942             });
43943         }
43944         if (this.footer) {
43945             this.assetHeight += this.footer.getHeight();
43946         }
43947         
43948     },
43949     onRenderList : function (  cls, i)
43950     {
43951         
43952         var lw = Math.floor(
43953                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43954         );
43955         
43956         this.list.setWidth(lw); // default to '1'
43957
43958         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43959         //il.on('mouseover', this.onViewOver, this, { list:  i });
43960         //il.on('mousemove', this.onViewMove, this, { list:  i });
43961         il.setWidth(lw);
43962         il.setStyle({ 'overflow-x' : 'hidden'});
43963
43964         if(!this.tpl){
43965             this.tpl = new Roo.Template({
43966                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43967                 isEmpty: function (value, allValues) {
43968                     //Roo.log(value);
43969                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43970                     return dl ? 'has-children' : 'no-children'
43971                 }
43972             });
43973         }
43974         
43975         var store  = this.store;
43976         if (i > 0) {
43977             store  = new Roo.data.SimpleStore({
43978                 //fields : this.store.reader.meta.fields,
43979                 reader : this.store.reader,
43980                 data : [ ]
43981             });
43982         }
43983         this.stores[i]  = store;
43984                   
43985         var view = this.views[i] = new Roo.View(
43986             il,
43987             this.tpl,
43988             {
43989                 singleSelect:true,
43990                 store: store,
43991                 selectedClass: this.selectedClass
43992             }
43993         );
43994         view.getEl().setWidth(lw);
43995         view.getEl().setStyle({
43996             position: i < 1 ? 'relative' : 'absolute',
43997             top: 0,
43998             left: (i * lw ) + 'px',
43999             display : i > 0 ? 'none' : 'block'
44000         });
44001         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44002         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44003         //view.on('click', this.onViewClick, this, { list : i });
44004
44005         store.on('beforeload', this.onBeforeLoad, this);
44006         store.on('load',  this.onLoad, this, { list  : i});
44007         store.on('loadexception', this.onLoadException, this);
44008
44009         // hide the other vies..
44010         
44011         
44012         
44013     },
44014       
44015     restrictHeight : function()
44016     {
44017         var mh = 0;
44018         Roo.each(this.innerLists, function(il,i) {
44019             var el = this.views[i].getEl();
44020             el.dom.style.height = '';
44021             var inner = el.dom;
44022             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44023             // only adjust heights on other ones..
44024             mh = Math.max(h, mh);
44025             if (i < 1) {
44026                 
44027                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44028                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44029                
44030             }
44031             
44032             
44033         }, this);
44034         
44035         this.list.beginUpdate();
44036         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44037         this.list.alignTo(this.el, this.listAlign);
44038         this.list.endUpdate();
44039         
44040     },
44041      
44042     
44043     // -- store handlers..
44044     // private
44045     onBeforeLoad : function()
44046     {
44047         if(!this.hasFocus){
44048             return;
44049         }
44050         this.innerLists[0].update(this.loadingText ?
44051                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44052         this.restrictHeight();
44053         this.selectedIndex = -1;
44054     },
44055     // private
44056     onLoad : function(a,b,c,d)
44057     {
44058         if (!this.loadingChildren) {
44059             // then we are loading the top level. - hide the children
44060             for (var i = 1;i < this.views.length; i++) {
44061                 this.views[i].getEl().setStyle({ display : 'none' });
44062             }
44063             var lw = Math.floor(
44064                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44065             );
44066         
44067              this.list.setWidth(lw); // default to '1'
44068
44069             
44070         }
44071         if(!this.hasFocus){
44072             return;
44073         }
44074         
44075         if(this.store.getCount() > 0) {
44076             this.expand();
44077             this.restrictHeight();   
44078         } else {
44079             this.onEmptyResults();
44080         }
44081         
44082         if (!this.loadingChildren) {
44083             this.selectActive();
44084         }
44085         /*
44086         this.stores[1].loadData([]);
44087         this.stores[2].loadData([]);
44088         this.views
44089         */    
44090     
44091         //this.el.focus();
44092     },
44093     
44094     
44095     // private
44096     onLoadException : function()
44097     {
44098         this.collapse();
44099         Roo.log(this.store.reader.jsonData);
44100         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44101             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44102         }
44103         
44104         
44105     },
44106     // no cleaning of leading spaces on blur here.
44107     cleanLeadingSpace : function(e) { },
44108     
44109
44110     onSelectChange : function (view, sels, opts )
44111     {
44112         var ix = view.getSelectedIndexes();
44113          
44114         if (opts.list > this.maxColumns - 2) {
44115             if (view.store.getCount()<  1) {
44116                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44117
44118             } else  {
44119                 if (ix.length) {
44120                     // used to clear ?? but if we are loading unselected 
44121                     this.setFromData(view.store.getAt(ix[0]).data);
44122                 }
44123                 
44124             }
44125             
44126             return;
44127         }
44128         
44129         if (!ix.length) {
44130             // this get's fired when trigger opens..
44131            // this.setFromData({});
44132             var str = this.stores[opts.list+1];
44133             str.data.clear(); // removeall wihtout the fire events..
44134             return;
44135         }
44136         
44137         var rec = view.store.getAt(ix[0]);
44138          
44139         this.setFromData(rec.data);
44140         this.fireEvent('select', this, rec, ix[0]);
44141         
44142         var lw = Math.floor(
44143              (
44144                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44145              ) / this.maxColumns
44146         );
44147         this.loadingChildren = true;
44148         this.stores[opts.list+1].loadDataFromChildren( rec );
44149         this.loadingChildren = false;
44150         var dl = this.stores[opts.list+1]. getTotalCount();
44151         
44152         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44153         
44154         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44155         for (var i = opts.list+2; i < this.views.length;i++) {
44156             this.views[i].getEl().setStyle({ display : 'none' });
44157         }
44158         
44159         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44160         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44161         
44162         if (this.isLoading) {
44163            // this.selectActive(opts.list);
44164         }
44165          
44166     },
44167     
44168     
44169     
44170     
44171     onDoubleClick : function()
44172     {
44173         this.collapse(); //??
44174     },
44175     
44176      
44177     
44178     
44179     
44180     // private
44181     recordToStack : function(store, prop, value, stack)
44182     {
44183         var cstore = new Roo.data.SimpleStore({
44184             //fields : this.store.reader.meta.fields, // we need array reader.. for
44185             reader : this.store.reader,
44186             data : [ ]
44187         });
44188         var _this = this;
44189         var record  = false;
44190         var srec = false;
44191         if(store.getCount() < 1){
44192             return false;
44193         }
44194         store.each(function(r){
44195             if(r.data[prop] == value){
44196                 record = r;
44197             srec = r;
44198                 return false;
44199             }
44200             if (r.data.cn && r.data.cn.length) {
44201                 cstore.loadDataFromChildren( r);
44202                 var cret = _this.recordToStack(cstore, prop, value, stack);
44203                 if (cret !== false) {
44204                     record = cret;
44205                     srec = r;
44206                     return false;
44207                 }
44208             }
44209              
44210             return true;
44211         });
44212         if (record == false) {
44213             return false
44214         }
44215         stack.unshift(srec);
44216         return record;
44217     },
44218     
44219     /*
44220      * find the stack of stores that match our value.
44221      *
44222      * 
44223      */
44224     
44225     selectActive : function ()
44226     {
44227         // if store is not loaded, then we will need to wait for that to happen first.
44228         var stack = [];
44229         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44230         for (var i = 0; i < stack.length; i++ ) {
44231             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44232         }
44233         
44234     }
44235         
44236          
44237     
44238     
44239     
44240     
44241 });/*
44242  * Based on:
44243  * Ext JS Library 1.1.1
44244  * Copyright(c) 2006-2007, Ext JS, LLC.
44245  *
44246  * Originally Released Under LGPL - original licence link has changed is not relivant.
44247  *
44248  * Fork - LGPL
44249  * <script type="text/javascript">
44250  */
44251 /**
44252  * @class Roo.form.Checkbox
44253  * @extends Roo.form.Field
44254  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44255  * @constructor
44256  * Creates a new Checkbox
44257  * @param {Object} config Configuration options
44258  */
44259 Roo.form.Checkbox = function(config){
44260     Roo.form.Checkbox.superclass.constructor.call(this, config);
44261     this.addEvents({
44262         /**
44263          * @event check
44264          * Fires when the checkbox is checked or unchecked.
44265              * @param {Roo.form.Checkbox} this This checkbox
44266              * @param {Boolean} checked The new checked value
44267              */
44268         check : true
44269     });
44270 };
44271
44272 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44273     /**
44274      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44275      */
44276     focusClass : undefined,
44277     /**
44278      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44279      */
44280     fieldClass: "x-form-field",
44281     /**
44282      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44283      */
44284     checked: false,
44285     /**
44286      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44287      * {tag: "input", type: "checkbox", autocomplete: "off"})
44288      */
44289     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44290     /**
44291      * @cfg {String} boxLabel The text that appears beside the checkbox
44292      */
44293     boxLabel : "",
44294     /**
44295      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44296      */  
44297     inputValue : '1',
44298     /**
44299      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44300      */
44301      valueOff: '0', // value when not checked..
44302
44303     actionMode : 'viewEl', 
44304     //
44305     // private
44306     itemCls : 'x-menu-check-item x-form-item',
44307     groupClass : 'x-menu-group-item',
44308     inputType : 'hidden',
44309     
44310     
44311     inSetChecked: false, // check that we are not calling self...
44312     
44313     inputElement: false, // real input element?
44314     basedOn: false, // ????
44315     
44316     isFormField: true, // not sure where this is needed!!!!
44317
44318     onResize : function(){
44319         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44320         if(!this.boxLabel){
44321             this.el.alignTo(this.wrap, 'c-c');
44322         }
44323     },
44324
44325     initEvents : function(){
44326         Roo.form.Checkbox.superclass.initEvents.call(this);
44327         this.el.on("click", this.onClick,  this);
44328         this.el.on("change", this.onClick,  this);
44329     },
44330
44331
44332     getResizeEl : function(){
44333         return this.wrap;
44334     },
44335
44336     getPositionEl : function(){
44337         return this.wrap;
44338     },
44339
44340     // private
44341     onRender : function(ct, position){
44342         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44343         /*
44344         if(this.inputValue !== undefined){
44345             this.el.dom.value = this.inputValue;
44346         }
44347         */
44348         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44349         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44350         var viewEl = this.wrap.createChild({ 
44351             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44352         this.viewEl = viewEl;   
44353         this.wrap.on('click', this.onClick,  this); 
44354         
44355         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44356         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44357         
44358         
44359         
44360         if(this.boxLabel){
44361             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44362         //    viewEl.on('click', this.onClick,  this); 
44363         }
44364         //if(this.checked){
44365             this.setChecked(this.checked);
44366         //}else{
44367             //this.checked = this.el.dom;
44368         //}
44369
44370     },
44371
44372     // private
44373     initValue : Roo.emptyFn,
44374
44375     /**
44376      * Returns the checked state of the checkbox.
44377      * @return {Boolean} True if checked, else false
44378      */
44379     getValue : function(){
44380         if(this.el){
44381             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44382         }
44383         return this.valueOff;
44384         
44385     },
44386
44387         // private
44388     onClick : function(){ 
44389         if (this.disabled) {
44390             return;
44391         }
44392         this.setChecked(!this.checked);
44393
44394         //if(this.el.dom.checked != this.checked){
44395         //    this.setValue(this.el.dom.checked);
44396        // }
44397     },
44398
44399     /**
44400      * Sets the checked state of the checkbox.
44401      * On is always based on a string comparison between inputValue and the param.
44402      * @param {Boolean/String} value - the value to set 
44403      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44404      */
44405     setValue : function(v,suppressEvent){
44406         
44407         
44408         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44409         //if(this.el && this.el.dom){
44410         //    this.el.dom.checked = this.checked;
44411         //    this.el.dom.defaultChecked = this.checked;
44412         //}
44413         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44414         //this.fireEvent("check", this, this.checked);
44415     },
44416     // private..
44417     setChecked : function(state,suppressEvent)
44418     {
44419         if (this.inSetChecked) {
44420             this.checked = state;
44421             return;
44422         }
44423         
44424     
44425         if(this.wrap){
44426             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44427         }
44428         this.checked = state;
44429         if(suppressEvent !== true){
44430             this.fireEvent('check', this, state);
44431         }
44432         this.inSetChecked = true;
44433         this.el.dom.value = state ? this.inputValue : this.valueOff;
44434         this.inSetChecked = false;
44435         
44436     },
44437     // handle setting of hidden value by some other method!!?!?
44438     setFromHidden: function()
44439     {
44440         if(!this.el){
44441             return;
44442         }
44443         //console.log("SET FROM HIDDEN");
44444         //alert('setFrom hidden');
44445         this.setValue(this.el.dom.value);
44446     },
44447     
44448     onDestroy : function()
44449     {
44450         if(this.viewEl){
44451             Roo.get(this.viewEl).remove();
44452         }
44453          
44454         Roo.form.Checkbox.superclass.onDestroy.call(this);
44455     },
44456     
44457     setBoxLabel : function(str)
44458     {
44459         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44460     }
44461
44462 });/*
44463  * Based on:
44464  * Ext JS Library 1.1.1
44465  * Copyright(c) 2006-2007, Ext JS, LLC.
44466  *
44467  * Originally Released Under LGPL - original licence link has changed is not relivant.
44468  *
44469  * Fork - LGPL
44470  * <script type="text/javascript">
44471  */
44472  
44473 /**
44474  * @class Roo.form.Radio
44475  * @extends Roo.form.Checkbox
44476  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44477  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44478  * @constructor
44479  * Creates a new Radio
44480  * @param {Object} config Configuration options
44481  */
44482 Roo.form.Radio = function(){
44483     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44484 };
44485 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44486     inputType: 'radio',
44487
44488     /**
44489      * If this radio is part of a group, it will return the selected value
44490      * @return {String}
44491      */
44492     getGroupValue : function(){
44493         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44494     },
44495     
44496     
44497     onRender : function(ct, position){
44498         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44499         
44500         if(this.inputValue !== undefined){
44501             this.el.dom.value = this.inputValue;
44502         }
44503          
44504         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44505         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44506         //var viewEl = this.wrap.createChild({ 
44507         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44508         //this.viewEl = viewEl;   
44509         //this.wrap.on('click', this.onClick,  this); 
44510         
44511         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44512         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44513         
44514         
44515         
44516         if(this.boxLabel){
44517             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44518         //    viewEl.on('click', this.onClick,  this); 
44519         }
44520          if(this.checked){
44521             this.el.dom.checked =   'checked' ;
44522         }
44523          
44524     } 
44525     
44526     
44527 });Roo.rtf = {}; // namespace
44528 Roo.rtf.Hex = function(hex)
44529 {
44530     this.hexstr = hex;
44531 };
44532 Roo.rtf.Paragraph = function(opts)
44533 {
44534     this.content = []; ///??? is that used?
44535 };Roo.rtf.Span = function(opts)
44536 {
44537     this.value = opts.value;
44538 };
44539
44540 Roo.rtf.Group = function(parent)
44541 {
44542     // we dont want to acutally store parent - it will make debug a nightmare..
44543     this.content = [];
44544     this.cn  = [];
44545      
44546        
44547     
44548 };
44549
44550 Roo.rtf.Group.prototype = {
44551     ignorable : false,
44552     content: false,
44553     cn: false,
44554     addContent : function(node) {
44555         // could set styles...
44556         this.content.push(node);
44557     },
44558     addChild : function(cn)
44559     {
44560         this.cn.push(cn);
44561     },
44562     // only for images really...
44563     toDataURL : function()
44564     {
44565         var mimetype = false;
44566         switch(true) {
44567             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44568                 mimetype = "image/png";
44569                 break;
44570              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44571                 mimetype = "image/jpeg";
44572                 break;
44573             default :
44574                 return 'about:blank'; // ?? error?
44575         }
44576         
44577         
44578         var hexstring = this.content[this.content.length-1].value;
44579         
44580         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44581             return String.fromCharCode(parseInt(a, 16));
44582         }).join(""));
44583     }
44584     
44585 };
44586 // this looks like it's normally the {rtf{ .... }}
44587 Roo.rtf.Document = function()
44588 {
44589     // we dont want to acutally store parent - it will make debug a nightmare..
44590     this.rtlch  = [];
44591     this.content = [];
44592     this.cn = [];
44593     
44594 };
44595 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44596     addChild : function(cn)
44597     {
44598         this.cn.push(cn);
44599         switch(cn.type) {
44600             case 'rtlch': // most content seems to be inside this??
44601             case 'listtext':
44602             case 'shpinst':
44603                 this.rtlch.push(cn);
44604                 return;
44605             default:
44606                 this[cn.type] = cn;
44607         }
44608         
44609     },
44610     
44611     getElementsByType : function(type)
44612     {
44613         var ret =  [];
44614         this._getElementsByType(type, ret, this.cn, 'rtf');
44615         return ret;
44616     },
44617     _getElementsByType : function (type, ret, search_array, path)
44618     {
44619         search_array.forEach(function(n,i) {
44620             if (n.type == type) {
44621                 n.path = path + '/' + n.type + ':' + i;
44622                 ret.push(n);
44623             }
44624             if (n.cn.length > 0) {
44625                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44626             }
44627         },this);
44628     }
44629     
44630 });
44631  
44632 Roo.rtf.Ctrl = function(opts)
44633 {
44634     this.value = opts.value;
44635     this.param = opts.param;
44636 };
44637 /**
44638  *
44639  *
44640  * based on this https://github.com/iarna/rtf-parser
44641  * it's really only designed to extract pict from pasted RTF 
44642  *
44643  * usage:
44644  *
44645  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44646  *  
44647  *
44648  */
44649
44650  
44651
44652
44653
44654 Roo.rtf.Parser = function(text) {
44655     //super({objectMode: true})
44656     this.text = '';
44657     this.parserState = this.parseText;
44658     
44659     // these are for interpeter...
44660     this.doc = {};
44661     ///this.parserState = this.parseTop
44662     this.groupStack = [];
44663     this.hexStore = [];
44664     this.doc = false;
44665     
44666     this.groups = []; // where we put the return.
44667     
44668     for (var ii = 0; ii < text.length; ++ii) {
44669         ++this.cpos;
44670         
44671         if (text[ii] === '\n') {
44672             ++this.row;
44673             this.col = 1;
44674         } else {
44675             ++this.col;
44676         }
44677         this.parserState(text[ii]);
44678     }
44679     
44680     
44681     
44682 };
44683 Roo.rtf.Parser.prototype = {
44684     text : '', // string being parsed..
44685     controlWord : '',
44686     controlWordParam :  '',
44687     hexChar : '',
44688     doc : false,
44689     group: false,
44690     groupStack : false,
44691     hexStore : false,
44692     
44693     
44694     cpos : 0, 
44695     row : 1, // reportin?
44696     col : 1, //
44697
44698      
44699     push : function (el)
44700     {
44701         var m = 'cmd'+ el.type;
44702         if (typeof(this[m]) == 'undefined') {
44703             Roo.log('invalid cmd:' + el.type);
44704             return;
44705         }
44706         this[m](el);
44707         //Roo.log(el);
44708     },
44709     flushHexStore : function()
44710     {
44711         if (this.hexStore.length < 1) {
44712             return;
44713         }
44714         var hexstr = this.hexStore.map(
44715             function(cmd) {
44716                 return cmd.value;
44717         }).join('');
44718         
44719         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44720               
44721             
44722         this.hexStore.splice(0)
44723         
44724     },
44725     
44726     cmdgroupstart : function()
44727     {
44728         this.flushHexStore();
44729         if (this.group) {
44730             this.groupStack.push(this.group);
44731         }
44732          // parent..
44733         if (this.doc === false) {
44734             this.group = this.doc = new Roo.rtf.Document();
44735             return;
44736             
44737         }
44738         this.group = new Roo.rtf.Group(this.group);
44739     },
44740     cmdignorable : function()
44741     {
44742         this.flushHexStore();
44743         this.group.ignorable = true;
44744     },
44745     cmdendparagraph : function()
44746     {
44747         this.flushHexStore();
44748         this.group.addContent(new Roo.rtf.Paragraph());
44749     },
44750     cmdgroupend : function ()
44751     {
44752         this.flushHexStore();
44753         var endingGroup = this.group;
44754         
44755         
44756         this.group = this.groupStack.pop();
44757         if (this.group) {
44758             this.group.addChild(endingGroup);
44759         }
44760         
44761         
44762         
44763         var doc = this.group || this.doc;
44764         //if (endingGroup instanceof FontTable) {
44765         //  doc.fonts = endingGroup.table
44766         //} else if (endingGroup instanceof ColorTable) {
44767         //  doc.colors = endingGroup.table
44768         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
44769         if (endingGroup.ignorable === false) {
44770             //code
44771             this.groups.push(endingGroup);
44772            // Roo.log( endingGroup );
44773         }
44774             //Roo.each(endingGroup.content, function(item)) {
44775             //    doc.addContent(item);
44776             //}
44777             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
44778         //}
44779     },
44780     cmdtext : function (cmd)
44781     {
44782         this.flushHexStore();
44783         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
44784             //this.group = this.doc
44785         }
44786         this.group.addContent(new Roo.rtf.Span(cmd));
44787     },
44788     cmdcontrolword : function (cmd)
44789     {
44790         this.flushHexStore();
44791         if (!this.group.type) {
44792             this.group.type = cmd.value;
44793             return;
44794         }
44795         this.group.addContent(new Roo.rtf.Ctrl(cmd));
44796         // we actually don't care about ctrl words...
44797         return ;
44798         /*
44799         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
44800         if (this[method]) {
44801             this[method](cmd.param)
44802         } else {
44803             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
44804         }
44805         */
44806     },
44807     cmdhexchar : function(cmd) {
44808         this.hexStore.push(cmd);
44809     },
44810     cmderror : function(cmd) {
44811         throw new Exception (cmd.value);
44812     },
44813     
44814     /*
44815       _flush (done) {
44816         if (this.text !== '\u0000') this.emitText()
44817         done()
44818       }
44819       */
44820       
44821       
44822     parseText : function(c)
44823     {
44824         if (c === '\\') {
44825             this.parserState = this.parseEscapes;
44826         } else if (c === '{') {
44827             this.emitStartGroup();
44828         } else if (c === '}') {
44829             this.emitEndGroup();
44830         } else if (c === '\x0A' || c === '\x0D') {
44831             // cr/lf are noise chars
44832         } else {
44833             this.text += c;
44834         }
44835     },
44836     
44837     parseEscapes: function (c)
44838     {
44839         if (c === '\\' || c === '{' || c === '}') {
44840             this.text += c;
44841             this.parserState = this.parseText;
44842         } else {
44843             this.parserState = this.parseControlSymbol;
44844             this.parseControlSymbol(c);
44845         }
44846     },
44847     parseControlSymbol: function(c)
44848     {
44849         if (c === '~') {
44850             this.text += '\u00a0'; // nbsp
44851             this.parserState = this.parseText
44852         } else if (c === '-') {
44853              this.text += '\u00ad'; // soft hyphen
44854         } else if (c === '_') {
44855             this.text += '\u2011'; // non-breaking hyphen
44856         } else if (c === '*') {
44857             this.emitIgnorable();
44858             this.parserState = this.parseText;
44859         } else if (c === "'") {
44860             this.parserState = this.parseHexChar;
44861         } else if (c === '|') { // formula cacter
44862             this.emitFormula();
44863             this.parserState = this.parseText;
44864         } else if (c === ':') { // subentry in an index entry
44865             this.emitIndexSubEntry();
44866             this.parserState = this.parseText;
44867         } else if (c === '\x0a') {
44868             this.emitEndParagraph();
44869             this.parserState = this.parseText;
44870         } else if (c === '\x0d') {
44871             this.emitEndParagraph();
44872             this.parserState = this.parseText;
44873         } else {
44874             this.parserState = this.parseControlWord;
44875             this.parseControlWord(c);
44876         }
44877     },
44878     parseHexChar: function (c)
44879     {
44880         if (/^[A-Fa-f0-9]$/.test(c)) {
44881             this.hexChar += c;
44882             if (this.hexChar.length >= 2) {
44883               this.emitHexChar();
44884               this.parserState = this.parseText;
44885             }
44886             return;
44887         }
44888         this.emitError("Invalid character \"" + c + "\" in hex literal.");
44889         this.parserState = this.parseText;
44890         
44891     },
44892     parseControlWord : function(c)
44893     {
44894         if (c === ' ') {
44895             this.emitControlWord();
44896             this.parserState = this.parseText;
44897         } else if (/^[-\d]$/.test(c)) {
44898             this.parserState = this.parseControlWordParam;
44899             this.controlWordParam += c;
44900         } else if (/^[A-Za-z]$/.test(c)) {
44901           this.controlWord += c;
44902         } else {
44903           this.emitControlWord();
44904           this.parserState = this.parseText;
44905           this.parseText(c);
44906         }
44907     },
44908     parseControlWordParam : function (c) {
44909         if (/^\d$/.test(c)) {
44910           this.controlWordParam += c;
44911         } else if (c === ' ') {
44912           this.emitControlWord();
44913           this.parserState = this.parseText;
44914         } else {
44915           this.emitControlWord();
44916           this.parserState = this.parseText;
44917           this.parseText(c);
44918         }
44919     },
44920     
44921     
44922     
44923     
44924     emitText : function () {
44925         if (this.text === '') {
44926             return;
44927         }
44928         this.push({
44929             type: 'text',
44930             value: this.text,
44931             pos: this.cpos,
44932             row: this.row,
44933             col: this.col
44934         });
44935         this.text = ''
44936     },
44937     emitControlWord : function ()
44938     {
44939         this.emitText();
44940         if (this.controlWord === '') {
44941             this.emitError('empty control word');
44942         } else {
44943             this.push({
44944                   type: 'controlword',
44945                   value: this.controlWord,
44946                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
44947                   pos: this.cpos,
44948                   row: this.row,
44949                   col: this.col
44950             });
44951         }
44952         this.controlWord = '';
44953         this.controlWordParam = '';
44954     },
44955     emitStartGroup : function ()
44956     {
44957         this.emitText();
44958         this.push({
44959             type: 'groupstart',
44960             pos: this.cpos,
44961             row: this.row,
44962             col: this.col
44963         });
44964     },
44965     emitEndGroup : function ()
44966     {
44967         this.emitText();
44968         this.push({
44969             type: 'groupend',
44970             pos: this.cpos,
44971             row: this.row,
44972             col: this.col
44973         });
44974     },
44975     emitIgnorable : function ()
44976     {
44977         this.emitText();
44978         this.push({
44979             type: 'ignorable',
44980             pos: this.cpos,
44981             row: this.row,
44982             col: this.col
44983         });
44984     },
44985     emitHexChar : function ()
44986     {
44987         this.emitText();
44988         this.push({
44989             type: 'hexchar',
44990             value: this.hexChar,
44991             pos: this.cpos,
44992             row: this.row,
44993             col: this.col
44994         });
44995         this.hexChar = ''
44996     },
44997     emitError : function (message)
44998     {
44999       this.emitText();
45000       this.push({
45001             type: 'error',
45002             value: message,
45003             row: this.row,
45004             col: this.col,
45005             char: this.cpos //,
45006             //stack: new Error().stack
45007         });
45008     },
45009     emitEndParagraph : function () {
45010         this.emitText();
45011         this.push({
45012             type: 'endparagraph',
45013             pos: this.cpos,
45014             row: this.row,
45015             col: this.col
45016         });
45017     }
45018      
45019 } ;
45020 Roo.htmleditor = {};
45021  
45022 /**
45023  * @class Roo.htmleditor.Filter
45024  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45025  * @cfg {DomElement} node The node to iterate and filter
45026  * @cfg {boolean|String|Array} tag Tags to replace 
45027  * @constructor
45028  * Create a new Filter.
45029  * @param {Object} config Configuration options
45030  */
45031
45032
45033
45034 Roo.htmleditor.Filter = function(cfg) {
45035     Roo.apply(this.cfg);
45036     // this does not actually call walk as it's really just a abstract class
45037 }
45038
45039
45040 Roo.htmleditor.Filter.prototype = {
45041     
45042     node: false,
45043     
45044     tag: false,
45045
45046     // overrride to do replace comments.
45047     replaceComment : false,
45048     
45049     // overrride to do replace or do stuff with tags..
45050     replaceTag : false,
45051     
45052     walk : function(dom)
45053     {
45054         Roo.each( Array.from(dom.childNodes), function( e ) {
45055             switch(true) {
45056                 
45057                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45058                     this.replaceComment(e);
45059                     return;
45060                 
45061                 case e.nodeType != 1: //not a node.
45062                     return;
45063                 
45064                 case this.tag === true: // everything
45065                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45066                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45067                     if (this.replaceTag && false === this.replaceTag(e)) {
45068                         return;
45069                     }
45070                     if (e.hasChildNodes()) {
45071                         this.walk(e);
45072                     }
45073                     return;
45074                 
45075                 default:    // tags .. that do not match.
45076                     if (e.hasChildNodes()) {
45077                         this.walk(e);
45078                     }
45079             }
45080             
45081         }, this);
45082         
45083     }
45084 }; 
45085
45086 /**
45087  * @class Roo.htmleditor.FilterAttributes
45088  * clean attributes and  styles including http:// etc.. in attribute
45089  * @constructor
45090 * Run a new Attribute Filter
45091 * @param {Object} config Configuration options
45092  */
45093 Roo.htmleditor.FilterAttributes = function(cfg)
45094 {
45095     Roo.apply(this, cfg);
45096     this.attrib_black = this.attrib_black || [];
45097     this.attrib_white = this.attrib_white || [];
45098
45099     this.attrib_clean = this.attrib_clean || [];
45100     this.style_white = this.style_white || [];
45101     this.style_black = this.style_black || [];
45102     this.walk(cfg.node);
45103 }
45104
45105 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45106 {
45107     tag: true, // all tags
45108     
45109     attrib_black : false, // array
45110     attrib_clean : false,
45111     attrib_white : false,
45112
45113     style_white : false,
45114     style_black : false,
45115      
45116      
45117     replaceTag : function(node)
45118     {
45119         if (!node.attributes || !node.attributes.length) {
45120             return true;
45121         }
45122         
45123         for (var i = node.attributes.length-1; i > -1 ; i--) {
45124             var a = node.attributes[i];
45125             //console.log(a);
45126             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45127                 node.removeAttribute(a.name);
45128                 continue;
45129             }
45130             
45131             
45132             
45133             if (a.name.toLowerCase().substr(0,2)=='on')  {
45134                 node.removeAttribute(a.name);
45135                 continue;
45136             }
45137             
45138             
45139             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45140                 node.removeAttribute(a.name);
45141                 continue;
45142             }
45143             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45144                 this.cleanAttr(node,a.name,a.value); // fixme..
45145                 continue;
45146             }
45147             if (a.name == 'style') {
45148                 this.cleanStyle(node,a.name,a.value);
45149                 continue;
45150             }
45151             /// clean up MS crap..
45152             // tecnically this should be a list of valid class'es..
45153             
45154             
45155             if (a.name == 'class') {
45156                 if (a.value.match(/^Mso/)) {
45157                     node.removeAttribute('class');
45158                 }
45159                 
45160                 if (a.value.match(/^body$/)) {
45161                     node.removeAttribute('class');
45162                 }
45163                 continue;
45164             }
45165             
45166             
45167             // style cleanup!?
45168             // class cleanup?
45169             
45170         }
45171         return true; // clean children
45172     },
45173         
45174     cleanAttr: function(node, n,v)
45175     {
45176         
45177         if (v.match(/^\./) || v.match(/^\//)) {
45178             return;
45179         }
45180         if (v.match(/^(http|https):\/\//)
45181             || v.match(/^mailto:/) 
45182             || v.match(/^ftp:/)
45183             || v.match(/^data:/)
45184             ) {
45185             return;
45186         }
45187         if (v.match(/^#/)) {
45188             return;
45189         }
45190         if (v.match(/^\{/)) { // allow template editing.
45191             return;
45192         }
45193 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45194         node.removeAttribute(n);
45195         
45196     },
45197     cleanStyle : function(node,  n,v)
45198     {
45199         if (v.match(/expression/)) { //XSS?? should we even bother..
45200             node.removeAttribute(n);
45201             return;
45202         }
45203         
45204         var parts = v.split(/;/);
45205         var clean = [];
45206         
45207         Roo.each(parts, function(p) {
45208             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45209             if (!p.length) {
45210                 return true;
45211             }
45212             var l = p.split(':').shift().replace(/\s+/g,'');
45213             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45214             
45215             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45216                 return true;
45217             }
45218             //Roo.log()
45219             // only allow 'c whitelisted system attributes'
45220             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45221                 return true;
45222             }
45223             
45224             
45225             clean.push(p);
45226             return true;
45227         },this);
45228         if (clean.length) { 
45229             node.setAttribute(n, clean.join(';'));
45230         } else {
45231             node.removeAttribute(n);
45232         }
45233         
45234     }
45235         
45236         
45237         
45238     
45239 });/**
45240  * @class Roo.htmleditor.FilterBlack
45241  * remove blacklisted elements.
45242  * @constructor
45243  * Run a new Blacklisted Filter
45244  * @param {Object} config Configuration options
45245  */
45246
45247 Roo.htmleditor.FilterBlack = function(cfg)
45248 {
45249     Roo.apply(this, cfg);
45250     this.walk(cfg.node);
45251 }
45252
45253 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45254 {
45255     tag : true, // all elements.
45256    
45257     replace : function(n)
45258     {
45259         n.parentNode.removeChild(n);
45260     }
45261 });
45262 /**
45263  * @class Roo.htmleditor.FilterComment
45264  * remove comments.
45265  * @constructor
45266 * Run a new Comments Filter
45267 * @param {Object} config Configuration options
45268  */
45269 Roo.htmleditor.FilterComment = function(cfg)
45270 {
45271     this.walk(cfg.node);
45272 }
45273
45274 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45275 {
45276   
45277     replaceComment : function(n)
45278     {
45279         n.parentNode.removeChild(n);
45280     }
45281 });/**
45282  * @class Roo.htmleditor.FilterKeepChildren
45283  * remove tags but keep children
45284  * @constructor
45285  * Run a new Keep Children Filter
45286  * @param {Object} config Configuration options
45287  */
45288
45289 Roo.htmleditor.FilterKeepChildren = function(cfg)
45290 {
45291     Roo.apply(this, cfg);
45292     if (this.tag === false) {
45293         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45294     }
45295     this.walk(cfg.node);
45296 }
45297
45298 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45299 {
45300     
45301   
45302     replaceTag : function(node)
45303     {
45304         // walk children...
45305         //Roo.log(node);
45306         var ar = Array.from(node.childNodes);
45307         //remove first..
45308         for (var i = 0; i < ar.length; i++) {
45309             if (ar[i].nodeType == 1) {
45310                 if (
45311                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45312                     || // array and it matches
45313                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45314                 ) {
45315                     this.replaceTag(ar[i]); // child is blacklisted as well...
45316                     continue;
45317                 }
45318             }
45319         }  
45320         ar = Array.from(node.childNodes);
45321         for (var i = 0; i < ar.length; i++) {
45322          
45323             node.removeChild(ar[i]);
45324             // what if we need to walk these???
45325             node.parentNode.insertBefore(ar[i], node);
45326             if (this.tag !== false) {
45327                 this.walk(ar[i]);
45328                 
45329             }
45330         }
45331         node.parentNode.removeChild(node);
45332         return false; // don't walk children
45333         
45334         
45335     }
45336 });/**
45337  * @class Roo.htmleditor.FilterParagraph
45338  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45339  * like on 'push' to remove the <p> tags and replace them with line breaks.
45340  * @constructor
45341  * Run a new Paragraph Filter
45342  * @param {Object} config Configuration options
45343  */
45344
45345 Roo.htmleditor.FilterParagraph = function(cfg)
45346 {
45347     // no need to apply config.
45348     this.walk(cfg.node);
45349 }
45350
45351 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45352 {
45353     
45354      
45355     tag : 'P',
45356     
45357      
45358     replaceTag : function(node)
45359     {
45360         
45361         if (node.childNodes.length == 1 &&
45362             node.childNodes[0].nodeType == 3 &&
45363             node.childNodes[0].textContent.trim().length < 1
45364             ) {
45365             // remove and replace with '<BR>';
45366             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45367             return false; // no need to walk..
45368         }
45369         var ar = Array.from(node.childNodes);
45370         for (var i = 0; i < ar.length; i++) {
45371             node.removeChild(ar[i]);
45372             // what if we need to walk these???
45373             node.parentNode.insertBefore(ar[i], node);
45374         }
45375         // now what about this?
45376         // <p> &nbsp; </p>
45377         
45378         // double BR.
45379         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45380         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45381         node.parentNode.removeChild(node);
45382         
45383         return false;
45384
45385     }
45386     
45387 });/**
45388  * @class Roo.htmleditor.FilterSpan
45389  * filter span's with no attributes out..
45390  * @constructor
45391  * Run a new Span Filter
45392  * @param {Object} config Configuration options
45393  */
45394
45395 Roo.htmleditor.FilterSpan = function(cfg)
45396 {
45397     // no need to apply config.
45398     this.walk(cfg.node);
45399 }
45400
45401 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45402 {
45403      
45404     tag : 'SPAN',
45405      
45406  
45407     replaceTag : function(node)
45408     {
45409         if (node.attributes && node.attributes.length > 0) {
45410             return true; // walk if there are any.
45411         }
45412         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45413         return false;
45414      
45415     }
45416     
45417 });/**
45418  * @class Roo.htmleditor.FilterTableWidth
45419   try and remove table width data - as that frequently messes up other stuff.
45420  * 
45421  *      was cleanTableWidths.
45422  *
45423  * Quite often pasting from word etc.. results in tables with column and widths.
45424  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45425  *
45426  * @constructor
45427  * Run a new Table Filter
45428  * @param {Object} config Configuration options
45429  */
45430
45431 Roo.htmleditor.FilterTableWidth = function(cfg)
45432 {
45433     // no need to apply config.
45434     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45435     this.walk(cfg.node);
45436 }
45437
45438 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45439 {
45440      
45441      
45442     
45443     replaceTag: function(node) {
45444         
45445         
45446       
45447         if (node.hasAttribute('width')) {
45448             node.removeAttribute('width');
45449         }
45450         
45451          
45452         if (node.hasAttribute("style")) {
45453             // pretty basic...
45454             
45455             var styles = node.getAttribute("style").split(";");
45456             var nstyle = [];
45457             Roo.each(styles, function(s) {
45458                 if (!s.match(/:/)) {
45459                     return;
45460                 }
45461                 var kv = s.split(":");
45462                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45463                     return;
45464                 }
45465                 // what ever is left... we allow.
45466                 nstyle.push(s);
45467             });
45468             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45469             if (!nstyle.length) {
45470                 node.removeAttribute('style');
45471             }
45472         }
45473         
45474         return true; // continue doing children..
45475     }
45476 });/**
45477  * @class Roo.htmleditor.FilterWord
45478  * try and clean up all the mess that Word generates.
45479  * 
45480  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45481  
45482  * @constructor
45483  * Run a new Span Filter
45484  * @param {Object} config Configuration options
45485  */
45486
45487 Roo.htmleditor.FilterWord = function(cfg)
45488 {
45489     // no need to apply config.
45490     this.walk(cfg.node);
45491 }
45492
45493 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45494 {
45495     tag: true,
45496      
45497     
45498     /**
45499      * Clean up MS wordisms...
45500      */
45501     replaceTag : function(node)
45502     {
45503          
45504         // no idea what this does - span with text, replaceds with just text.
45505         if(
45506                 node.nodeName == 'SPAN' &&
45507                 !node.hasAttributes() &&
45508                 node.childNodes.length == 1 &&
45509                 node.firstChild.nodeName == "#text"  
45510         ) {
45511             var textNode = node.firstChild;
45512             node.removeChild(textNode);
45513             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45514                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45515             }
45516             node.parentNode.insertBefore(textNode, node);
45517             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45518                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45519             }
45520             
45521             node.parentNode.removeChild(node);
45522             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45523         }
45524         
45525    
45526         
45527         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45528             node.parentNode.removeChild(node);
45529             return false; // dont do chidlren
45530         }
45531         //Roo.log(node.tagName);
45532         // remove - but keep children..
45533         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45534             //Roo.log('-- removed');
45535             while (node.childNodes.length) {
45536                 var cn = node.childNodes[0];
45537                 node.removeChild(cn);
45538                 node.parentNode.insertBefore(cn, node);
45539                 // move node to parent - and clean it..
45540                 this.replaceTag(cn);
45541             }
45542             node.parentNode.removeChild(node);
45543             /// no need to iterate chidlren = it's got none..
45544             //this.iterateChildren(node, this.cleanWord);
45545             return false; // no need to iterate children.
45546         }
45547         // clean styles
45548         if (node.className.length) {
45549             
45550             var cn = node.className.split(/\W+/);
45551             var cna = [];
45552             Roo.each(cn, function(cls) {
45553                 if (cls.match(/Mso[a-zA-Z]+/)) {
45554                     return;
45555                 }
45556                 cna.push(cls);
45557             });
45558             node.className = cna.length ? cna.join(' ') : '';
45559             if (!cna.length) {
45560                 node.removeAttribute("class");
45561             }
45562         }
45563         
45564         if (node.hasAttribute("lang")) {
45565             node.removeAttribute("lang");
45566         }
45567         
45568         if (node.hasAttribute("style")) {
45569             
45570             var styles = node.getAttribute("style").split(";");
45571             var nstyle = [];
45572             Roo.each(styles, function(s) {
45573                 if (!s.match(/:/)) {
45574                     return;
45575                 }
45576                 var kv = s.split(":");
45577                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45578                     return;
45579                 }
45580                 // what ever is left... we allow.
45581                 nstyle.push(s);
45582             });
45583             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45584             if (!nstyle.length) {
45585                 node.removeAttribute('style');
45586             }
45587         }
45588         return true; // do children
45589         
45590         
45591         
45592     }
45593 });
45594 /**
45595  * @class Roo.htmleditor.FilterStyleToTag
45596  * part of the word stuff... - certain 'styles' should be converted to tags.
45597  * eg.
45598  *   font-weight: bold -> bold
45599  *   ?? super / subscrit etc..
45600  * 
45601  * @constructor
45602 * Run a new style to tag filter.
45603 * @param {Object} config Configuration options
45604  */
45605 Roo.htmleditor.FilterStyleToTag = function(cfg)
45606 {
45607     
45608     this.tags = {
45609         B  : [ 'fontWeight' , 'bold'],
45610         I :  [ 'fontStyle' , 'italic'],
45611         //pre :  [ 'font-style' , 'italic'],
45612         // h1.. h6 ?? font-size?
45613         SUP : [ 'verticalAlign' , 'super' ],
45614         SUB : [ 'verticalAlign' , 'sub' ]
45615         
45616         
45617     };
45618     
45619     Roo.apply(this, cfg);
45620      
45621     
45622     this.walk(cfg.node);
45623     
45624     
45625     
45626 }
45627
45628
45629 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45630 {
45631     tag: true, // all tags
45632     
45633     tags : false,
45634     
45635     
45636     replaceTag : function(node)
45637     {
45638         
45639         
45640         if (node.getAttribute("style") === null) {
45641             return true;
45642         }
45643         var inject = [];
45644         for (var k in this.tags) {
45645             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45646                 inject.push(k);
45647                 node.style.removeProperty(this.tags[k][0]);
45648             }
45649         }
45650         if (!inject.length) {
45651             return true; 
45652         }
45653         var cn = Array.from(node.childNodes);
45654         var nn = node;
45655         Roo.each(inject, function(t) {
45656             var nc = node.ownerDocument.createelement(t);
45657             nn.appendChild(nc);
45658             nn = nc;
45659         });
45660         for(var i = 0;i < cn.length;cn++) {
45661             node.removeChild(cn[i]);
45662             nn.appendChild(cn[i]);
45663         }
45664         return true /// iterate thru
45665     }
45666     
45667 })/**
45668  * @class Roo.htmleditor.FilterLongBr
45669  * BR/BR/BR - keep a maximum of 2...
45670  * @constructor
45671  * Run a new Long BR Filter
45672  * @param {Object} config Configuration options
45673  */
45674
45675 Roo.htmleditor.FilterLongBr = function(cfg)
45676 {
45677     // no need to apply config.
45678     this.walk(cfg.node);
45679 }
45680
45681 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45682 {
45683     
45684      
45685     tag : 'BR',
45686     
45687      
45688     replaceTag : function(node)
45689     {
45690         
45691         var ps = node.nextSibling;
45692         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45693             ps = ps.nextSibling;
45694         }
45695         
45696         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45697             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45698             return false;
45699         }
45700         
45701         if (!ps || ps.nodeType != 1) {
45702             return false;
45703         }
45704         
45705         if (!ps || ps.tagName != 'BR') {
45706            
45707             return false;
45708         }
45709         
45710         
45711         
45712         
45713         
45714         if (!node.previousSibling) {
45715             return false;
45716         }
45717         var ps = node.previousSibling;
45718         
45719         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45720             ps = ps.previousSibling;
45721         }
45722         if (!ps || ps.nodeType != 1) {
45723             return false;
45724         }
45725         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45726         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45727             return false;
45728         }
45729         
45730         node.parentNode.removeChild(node); // remove me...
45731         
45732         return false; // no need to do children
45733
45734     }
45735     
45736 });
45737 /**
45738  * @class Roo.htmleditor.Tidy
45739  * Tidy HTML 
45740  * @cfg {Roo.HtmlEditorCore} core the editor.
45741  * @constructor
45742  * Create a new Filter.
45743  * @param {Object} config Configuration options
45744  */
45745
45746
45747 Roo.htmleditor.Tidy = function(cfg) {
45748     Roo.apply(this, cfg);
45749     
45750     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45751      
45752 }
45753
45754 Roo.htmleditor.Tidy.toString = function(node)
45755 {
45756     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45757 }
45758
45759 Roo.htmleditor.Tidy.prototype = {
45760     
45761     
45762     wrap : function(s) {
45763         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45764     },
45765
45766     
45767     tidy : function(node, indent) {
45768      
45769         if  (node.nodeType == 3) {
45770             // text.
45771             
45772             
45773             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45774                 
45775             
45776         }
45777         
45778         if  (node.nodeType != 1) {
45779             return '';
45780         }
45781         
45782         
45783         
45784         if (node.tagName == 'BODY') {
45785             
45786             return this.cn(node, '');
45787         }
45788              
45789              // Prints the node tagName, such as <A>, <IMG>, etc
45790         var ret = "<" + node.tagName +  this.attr(node) ;
45791         
45792         // elements with no children..
45793         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45794                 return ret + '/>';
45795         }
45796         ret += '>';
45797         
45798         
45799         var cindent = indent === false ? '' : (indent + '  ');
45800         // tags where we will not pad the children.. (inline text tags etc..)
45801         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45802             cindent = false;
45803             
45804             
45805         }
45806         
45807         var cn = this.cn(node, cindent );
45808         
45809         return ret + cn  + '</' + node.tagName + '>';
45810         
45811     },
45812     cn: function(node, indent)
45813     {
45814         var ret = [];
45815         
45816         var ar = Array.from(node.childNodes);
45817         for (var i = 0 ; i < ar.length ; i++) {
45818             
45819             
45820             
45821             if (indent !== false   // indent==false preservies everything
45822                 && i > 0
45823                 && ar[i].nodeType == 3 
45824                 && ar[i].nodeValue.length > 0
45825                 && ar[i].nodeValue.match(/^\s+/)
45826             ) {
45827                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45828                     ret.pop(); // remove line break from last?
45829                 }
45830                 
45831                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45832             }
45833             if (indent !== false
45834                 && ar[i].nodeType == 1 // element - and indent is not set... 
45835             ) {
45836                 ret.push("\n" + indent); 
45837             }
45838             
45839             ret.push(this.tidy(ar[i], indent));
45840             // text + trailing indent 
45841             if (indent !== false
45842                 && ar[i].nodeType == 3
45843                 && ar[i].nodeValue.length > 0
45844                 && ar[i].nodeValue.match(/\s+$/)
45845             ){
45846                 ret.push("\n" + indent); 
45847             }
45848             
45849             
45850             
45851             
45852         }
45853         // what if all text?
45854         
45855         
45856         return ret.join('');
45857     },
45858     
45859          
45860         
45861     attr : function(node)
45862     {
45863         var attr = [];
45864         for(i = 0; i < node.attributes.length;i++) {
45865             
45866             // skip empty values?
45867             if (!node.attributes.item(i).value.length) {
45868                 continue;
45869             }
45870             attr.push(  node.attributes.item(i).name + '="' +
45871                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45872             );
45873         }
45874         return attr.length ? (' ' + attr.join(' ') ) : '';
45875         
45876     }
45877     
45878     
45879     
45880 }
45881 /**
45882  * @class Roo.htmleditor.KeyEnter
45883  * Handle Enter press..
45884  * @cfg {Roo.HtmlEditorCore} core the editor.
45885  * @constructor
45886  * Create a new Filter.
45887  * @param {Object} config Configuration options
45888  */
45889
45890
45891
45892 Roo.htmleditor.KeyEnter = function(cfg) {
45893     Roo.apply(this, cfg);
45894     // this does not actually call walk as it's really just a abstract class
45895  
45896     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45897 }
45898
45899
45900 Roo.htmleditor.KeyEnter.prototype = {
45901     
45902     core : false,
45903     
45904     keypress : function(e) {
45905         if (e.charCode != 13) {
45906             return true;
45907         }
45908         e.preventDefault();
45909         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45910         var doc = this.core.doc;
45911         
45912         var docFragment = doc.createDocumentFragment();
45913     
45914         //add a new line
45915         var newEle = doc.createTextNode('\n');
45916         docFragment.appendChild(newEle);
45917     
45918     
45919         var range = this.core.win.getSelection().getRangeAt(0);
45920         var n = range.commonAncestorContainer ;
45921         while (n && n.nodeType != 1) {
45922             n  = n.parentNode;
45923         }
45924         var li = false;
45925         if (n && n.tagName == 'UL') {
45926             li = doc.createElement('LI');
45927             n.appendChild(li);
45928             
45929         }
45930         if (n && n.tagName == 'LI') {
45931             li = doc.createElement('LI');
45932             if (n.nextSibling) {
45933                 n.parentNode.insertBefore(li, n.firstSibling);
45934                 
45935             } else {
45936                 n.parentNode.appendChild(li);
45937             }
45938         }
45939         if (li) {   
45940             range = doc.createRange();
45941             range.setStartAfter(li);
45942             range.collapse(true);
45943         
45944             //make the cursor there
45945             var sel = this.core.win.getSelection();
45946             sel.removeAllRanges();
45947             sel.addRange(range);
45948             return false;
45949             
45950             
45951         }
45952         //add the br, or p, or something else
45953         newEle = doc.createElement('br');
45954         docFragment.appendChild(newEle);
45955     
45956         //make the br replace selection
45957         
45958         range.deleteContents();
45959         
45960         range.insertNode(docFragment);
45961     
45962         //create a new range
45963         range = doc.createRange();
45964         range.setStartAfter(newEle);
45965         range.collapse(true);
45966     
45967         //make the cursor there
45968         var sel = this.core.win.getSelection();
45969         sel.removeAllRanges();
45970         sel.addRange(range);
45971     
45972         return false;
45973          
45974     }
45975 };
45976      
45977 /**
45978  * @class Roo.htmleditor.Block
45979  * Base class for html editor blocks - do not use it directly .. extend it..
45980  * @cfg {DomElement} node The node to apply stuff to.
45981  * @cfg {String} friendly_name the name that appears in the context bar about this block
45982  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
45983  
45984  * @constructor
45985  * Create a new Filter.
45986  * @param {Object} config Configuration options
45987  */
45988
45989 Roo.htmleditor.Block  = function(cfg)
45990 {
45991     // do nothing .. should not be called really.
45992 }
45993
45994 Roo.htmleditor.Block.factory = function(node)
45995 {
45996     
45997     var id = Roo.get(node).id;
45998     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
45999         Roo.htmleditor.Block.cache[id].readElement();
46000         return Roo.htmleditor.Block.cache[id];
46001     }
46002     
46003     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46004     if (typeof(cls) == 'undefined') {
46005         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46006         return false;
46007     }
46008     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46009     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46010 };
46011 // question goes here... do we need to clear out this cache sometimes?
46012 // or show we make it relivant to the htmleditor.
46013 Roo.htmleditor.Block.cache = {};
46014
46015 Roo.htmleditor.Block.prototype = {
46016     
46017     node : false,
46018     
46019      // used by context menu
46020     friendly_name : 'Image with caption',
46021     
46022     context : false,
46023     /**
46024      * Update a node with values from this object
46025      * @param {DomElement} node
46026      */
46027     updateElement : function(node)
46028     {
46029         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46030     },
46031      /**
46032      * convert to plain HTML for calling insertAtCursor..
46033      */
46034     toHTML : function()
46035     {
46036         return Roo.DomHelper.markup(this.toObject());
46037     },
46038     /**
46039      * used by readEleemnt to extract data from a node
46040      * may need improving as it's pretty basic
46041      
46042      * @param {DomElement} node
46043      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46044      * @param {String} attribute (use html - for contents, or style for using next param as style)
46045      * @param {String} style the style property - eg. text-align
46046      */
46047     getVal : function(node, tag, attr, style)
46048     {
46049         var n = node;
46050         if (tag !== true && n.tagName != tag.toUpperCase()) {
46051             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46052             // but kiss for now.
46053             n = node.getElementsByTagName(tag).item(0);
46054         }
46055         if (attr == 'html') {
46056             return n.innerHTML;
46057         }
46058         if (attr == 'style') {
46059             return Roo.get(n).getStyle(style);
46060         }
46061         
46062         return Roo.get(n).attr(attr);
46063             
46064     },
46065     /**
46066      * create a DomHelper friendly object - for use with 
46067      * Roo.DomHelper.markup / overwrite / etc..
46068      * (override this)
46069      */
46070     toObject : function()
46071     {
46072         return {};
46073     },
46074       /**
46075      * Read a node that has a 'data-block' property - and extract the values from it.
46076      * @param {DomElement} node - the node
46077      */
46078     readElement : function(node)
46079     {
46080         
46081     } 
46082     
46083     
46084 };
46085
46086  
46087
46088 /**
46089  * @class Roo.htmleditor.BlockFigure
46090  * Block that has an image and a figcaption
46091  * @cfg {String} image_src the url for the image
46092  * @cfg {String} align (left|right) alignment for the block default left
46093  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46094  * @cfg {String} caption the text to appear below  (and in the alt tag)
46095  * @cfg {String|number} image_width the width of the image number or %?
46096  * @cfg {String|number} image_height the height of the image number or %?
46097  * 
46098  * @constructor
46099  * Create a new Filter.
46100  * @param {Object} config Configuration options
46101  */
46102
46103 Roo.htmleditor.BlockFigure = function(cfg)
46104 {
46105     if (cfg.node) {
46106         this.readElement(cfg.node);
46107         this.updateElement(cfg.node);
46108     }
46109     Roo.apply(this, cfg);
46110 }
46111 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46112  
46113     
46114     // setable values.
46115     image_src: '',
46116     
46117     align: 'left',
46118     caption : '',
46119     text_align: 'left',
46120     
46121     width : '46%',
46122     margin: '2%',
46123     
46124     // used by context menu
46125     friendly_name : 'Image with caption',
46126     
46127     context : { // ?? static really
46128         width : {
46129             title: "Width",
46130             width: 40
46131             // ?? number
46132         },
46133         margin : {
46134             title: "Margin",
46135             width: 40
46136             // ?? number
46137         },
46138         align: {
46139             title: "Align",
46140             opts : [[ "left"],[ "right"]],
46141             width : 80
46142             
46143         },
46144         text_align: {
46145             title: "Caption Align",
46146             opts : [ [ "left"],[ "right"],[ "center"]],
46147             width : 80
46148         },
46149         
46150        
46151         image_src : {
46152             title: "Src",
46153             width: 220
46154         }
46155     },
46156     /**
46157      * create a DomHelper friendly object - for use with
46158      * Roo.DomHelper.markup / overwrite / etc..
46159      */
46160     toObject : function()
46161     {
46162         var d = document.createElement('div');
46163         d.innerHTML = this.caption;
46164         
46165         return {
46166             tag: 'figure',
46167             'data-block' : 'Figure',
46168             contenteditable : 'false',
46169             style : {
46170                 display: 'table',
46171                 float :  this.align ,
46172                 width :  this.width,
46173                 margin:  this.margin
46174             },
46175             cn : [
46176                 {
46177                     tag : 'img',
46178                     src : this.image_src,
46179                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46180                     style: {
46181                         width: '100%'
46182                     }
46183                 },
46184                 {
46185                     tag: 'figcaption',
46186                     contenteditable : true,
46187                     style : {
46188                         'text-align': this.text_align
46189                     },
46190                     html : this.caption
46191                     
46192                 }
46193             ]
46194         };
46195     },
46196     
46197     readElement : function(node)
46198     {
46199         this.image_src = this.getVal(node, 'img', 'src');
46200         this.align = this.getVal(node, 'figure', 'style', 'float');
46201         this.caption = this.getVal(node, 'figcaption', 'html');
46202         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46203         this.width = this.getVal(node, 'figure', 'style', 'width');
46204         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46205         
46206     } 
46207     
46208   
46209    
46210      
46211     
46212     
46213     
46214     
46215 })
46216
46217 //<script type="text/javascript">
46218
46219 /*
46220  * Based  Ext JS Library 1.1.1
46221  * Copyright(c) 2006-2007, Ext JS, LLC.
46222  * LGPL
46223  *
46224  */
46225  
46226 /**
46227  * @class Roo.HtmlEditorCore
46228  * @extends Roo.Component
46229  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46230  *
46231  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46232  */
46233
46234 Roo.HtmlEditorCore = function(config){
46235     
46236     
46237     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46238     
46239     
46240     this.addEvents({
46241         /**
46242          * @event initialize
46243          * Fires when the editor is fully initialized (including the iframe)
46244          * @param {Roo.HtmlEditorCore} this
46245          */
46246         initialize: true,
46247         /**
46248          * @event activate
46249          * Fires when the editor is first receives the focus. Any insertion must wait
46250          * until after this event.
46251          * @param {Roo.HtmlEditorCore} this
46252          */
46253         activate: true,
46254          /**
46255          * @event beforesync
46256          * Fires before the textarea is updated with content from the editor iframe. Return false
46257          * to cancel the sync.
46258          * @param {Roo.HtmlEditorCore} this
46259          * @param {String} html
46260          */
46261         beforesync: true,
46262          /**
46263          * @event beforepush
46264          * Fires before the iframe editor is updated with content from the textarea. Return false
46265          * to cancel the push.
46266          * @param {Roo.HtmlEditorCore} this
46267          * @param {String} html
46268          */
46269         beforepush: true,
46270          /**
46271          * @event sync
46272          * Fires when the textarea is updated with content from the editor iframe.
46273          * @param {Roo.HtmlEditorCore} this
46274          * @param {String} html
46275          */
46276         sync: true,
46277          /**
46278          * @event push
46279          * Fires when the iframe editor is updated with content from the textarea.
46280          * @param {Roo.HtmlEditorCore} this
46281          * @param {String} html
46282          */
46283         push: true,
46284         
46285         /**
46286          * @event editorevent
46287          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46288          * @param {Roo.HtmlEditorCore} this
46289          */
46290         editorevent: true
46291         
46292     });
46293     
46294     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46295     
46296     // defaults : white / black...
46297     this.applyBlacklists();
46298     
46299     
46300     
46301 };
46302
46303
46304 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46305
46306
46307      /**
46308      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46309      */
46310     
46311     owner : false,
46312     
46313      /**
46314      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46315      *                        Roo.resizable.
46316      */
46317     resizable : false,
46318      /**
46319      * @cfg {Number} height (in pixels)
46320      */   
46321     height: 300,
46322    /**
46323      * @cfg {Number} width (in pixels)
46324      */   
46325     width: 500,
46326     
46327     /**
46328      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46329      * 
46330      */
46331     stylesheets: false,
46332     
46333     /**
46334      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46335      */
46336     allowComments: false,
46337     // id of frame..
46338     frameId: false,
46339     
46340     // private properties
46341     validationEvent : false,
46342     deferHeight: true,
46343     initialized : false,
46344     activated : false,
46345     sourceEditMode : false,
46346     onFocus : Roo.emptyFn,
46347     iframePad:3,
46348     hideMode:'offsets',
46349     
46350     clearUp: true,
46351     
46352     // blacklist + whitelisted elements..
46353     black: false,
46354     white: false,
46355      
46356     bodyCls : '',
46357
46358     /**
46359      * Protected method that will not generally be called directly. It
46360      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46361      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46362      */
46363     getDocMarkup : function(){
46364         // body styles..
46365         var st = '';
46366         
46367         // inherit styels from page...?? 
46368         if (this.stylesheets === false) {
46369             
46370             Roo.get(document.head).select('style').each(function(node) {
46371                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46372             });
46373             
46374             Roo.get(document.head).select('link').each(function(node) { 
46375                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46376             });
46377             
46378         } else if (!this.stylesheets.length) {
46379                 // simple..
46380                 st = '<style type="text/css">' +
46381                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46382                    '</style>';
46383         } else {
46384             for (var i in this.stylesheets) {
46385                 if (typeof(this.stylesheets[i]) != 'string') {
46386                     continue;
46387                 }
46388                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46389             }
46390             
46391         }
46392         
46393         st +=  '<style type="text/css">' +
46394             'IMG { cursor: pointer } ' +
46395         '</style>';
46396
46397         var cls = 'roo-htmleditor-body';
46398         
46399         if(this.bodyCls.length){
46400             cls += ' ' + this.bodyCls;
46401         }
46402         
46403         return '<html><head>' + st  +
46404             //<style type="text/css">' +
46405             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46406             //'</style>' +
46407             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46408     },
46409
46410     // private
46411     onRender : function(ct, position)
46412     {
46413         var _t = this;
46414         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46415         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46416         
46417         
46418         this.el.dom.style.border = '0 none';
46419         this.el.dom.setAttribute('tabIndex', -1);
46420         this.el.addClass('x-hidden hide');
46421         
46422         
46423         
46424         if(Roo.isIE){ // fix IE 1px bogus margin
46425             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46426         }
46427        
46428         
46429         this.frameId = Roo.id();
46430         
46431          
46432         
46433         var iframe = this.owner.wrap.createChild({
46434             tag: 'iframe',
46435             cls: 'form-control', // bootstrap..
46436             id: this.frameId,
46437             name: this.frameId,
46438             frameBorder : 'no',
46439             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46440         }, this.el
46441         );
46442         
46443         
46444         this.iframe = iframe.dom;
46445
46446         this.assignDocWin();
46447         
46448         this.doc.designMode = 'on';
46449        
46450         this.doc.open();
46451         this.doc.write(this.getDocMarkup());
46452         this.doc.close();
46453
46454         
46455         var task = { // must defer to wait for browser to be ready
46456             run : function(){
46457                 //console.log("run task?" + this.doc.readyState);
46458                 this.assignDocWin();
46459                 if(this.doc.body || this.doc.readyState == 'complete'){
46460                     try {
46461                         this.doc.designMode="on";
46462                     } catch (e) {
46463                         return;
46464                     }
46465                     Roo.TaskMgr.stop(task);
46466                     this.initEditor.defer(10, this);
46467                 }
46468             },
46469             interval : 10,
46470             duration: 10000,
46471             scope: this
46472         };
46473         Roo.TaskMgr.start(task);
46474
46475     },
46476
46477     // private
46478     onResize : function(w, h)
46479     {
46480          Roo.log('resize: ' +w + ',' + h );
46481         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46482         if(!this.iframe){
46483             return;
46484         }
46485         if(typeof w == 'number'){
46486             
46487             this.iframe.style.width = w + 'px';
46488         }
46489         if(typeof h == 'number'){
46490             
46491             this.iframe.style.height = h + 'px';
46492             if(this.doc){
46493                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46494             }
46495         }
46496         
46497     },
46498
46499     /**
46500      * Toggles the editor between standard and source edit mode.
46501      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46502      */
46503     toggleSourceEdit : function(sourceEditMode){
46504         
46505         this.sourceEditMode = sourceEditMode === true;
46506         
46507         if(this.sourceEditMode){
46508  
46509             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46510             
46511         }else{
46512             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46513             //this.iframe.className = '';
46514             this.deferFocus();
46515         }
46516         //this.setSize(this.owner.wrap.getSize());
46517         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46518     },
46519
46520     
46521   
46522
46523     /**
46524      * Protected method that will not generally be called directly. If you need/want
46525      * custom HTML cleanup, this is the method you should override.
46526      * @param {String} html The HTML to be cleaned
46527      * return {String} The cleaned HTML
46528      */
46529     cleanHtml : function(html){
46530         html = String(html);
46531         if(html.length > 5){
46532             if(Roo.isSafari){ // strip safari nonsense
46533                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46534             }
46535         }
46536         if(html == '&nbsp;'){
46537             html = '';
46538         }
46539         return html;
46540     },
46541
46542     /**
46543      * HTML Editor -> Textarea
46544      * Protected method that will not generally be called directly. Syncs the contents
46545      * of the editor iframe with the textarea.
46546      */
46547     syncValue : function()
46548     {
46549         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46550         if(this.initialized){
46551             var bd = (this.doc.body || this.doc.documentElement);
46552             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46553             
46554             // not sure if this is really the place for this
46555             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46556             // this has to update attributes that get duped.. like alt and caption..
46557             
46558             
46559             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46560             //     Roo.htmleditor.Block.factory(e);
46561             //},this);
46562             
46563             
46564             var div = document.createElement('div');
46565             div.innerHTML = bd.innerHTML;
46566             // remove content editable. (blocks)
46567             
46568            
46569             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46570             //?? tidy?
46571             var html = div.innerHTML;
46572             if(Roo.isSafari){
46573                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46574                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46575                 if(m && m[1]){
46576                     html = '<div style="'+m[0]+'">' + html + '</div>';
46577                 }
46578             }
46579             html = this.cleanHtml(html);
46580             // fix up the special chars.. normaly like back quotes in word...
46581             // however we do not want to do this with chinese..
46582             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46583                 
46584                 var cc = match.charCodeAt();
46585
46586                 // Get the character value, handling surrogate pairs
46587                 if (match.length == 2) {
46588                     // It's a surrogate pair, calculate the Unicode code point
46589                     var high = match.charCodeAt(0) - 0xD800;
46590                     var low  = match.charCodeAt(1) - 0xDC00;
46591                     cc = (high * 0x400) + low + 0x10000;
46592                 }  else if (
46593                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46594                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46595                     (cc >= 0xf900 && cc < 0xfb00 )
46596                 ) {
46597                         return match;
46598                 }  
46599          
46600                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46601                 return "&#" + cc + ";";
46602                 
46603                 
46604             });
46605             
46606             
46607              
46608             if(this.owner.fireEvent('beforesync', this, html) !== false){
46609                 this.el.dom.value = html;
46610                 this.owner.fireEvent('sync', this, html);
46611             }
46612         }
46613     },
46614
46615     /**
46616      * TEXTAREA -> EDITABLE
46617      * Protected method that will not generally be called directly. Pushes the value of the textarea
46618      * into the iframe editor.
46619      */
46620     pushValue : function()
46621     {
46622         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46623         if(this.initialized){
46624             var v = this.el.dom.value.trim();
46625             
46626             
46627             if(this.owner.fireEvent('beforepush', this, v) !== false){
46628                 var d = (this.doc.body || this.doc.documentElement);
46629                 d.innerHTML = v;
46630                  
46631                 this.el.dom.value = d.innerHTML;
46632                 this.owner.fireEvent('push', this, v);
46633             }
46634             
46635             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46636                 
46637                 Roo.htmleditor.Block.factory(e);
46638                 
46639             },this);
46640             var lc = this.doc.body.lastChild;
46641             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46642                 // add an extra line at the end.
46643                 this.doc.body.appendChild(this.doc.createElement('br'));
46644             }
46645             
46646             
46647         }
46648     },
46649
46650     // private
46651     deferFocus : function(){
46652         this.focus.defer(10, this);
46653     },
46654
46655     // doc'ed in Field
46656     focus : function(){
46657         if(this.win && !this.sourceEditMode){
46658             this.win.focus();
46659         }else{
46660             this.el.focus();
46661         }
46662     },
46663     
46664     assignDocWin: function()
46665     {
46666         var iframe = this.iframe;
46667         
46668          if(Roo.isIE){
46669             this.doc = iframe.contentWindow.document;
46670             this.win = iframe.contentWindow;
46671         } else {
46672 //            if (!Roo.get(this.frameId)) {
46673 //                return;
46674 //            }
46675 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46676 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46677             
46678             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46679                 return;
46680             }
46681             
46682             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46683             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46684         }
46685     },
46686     
46687     // private
46688     initEditor : function(){
46689         //console.log("INIT EDITOR");
46690         this.assignDocWin();
46691         
46692         
46693         
46694         this.doc.designMode="on";
46695         this.doc.open();
46696         this.doc.write(this.getDocMarkup());
46697         this.doc.close();
46698         
46699         var dbody = (this.doc.body || this.doc.documentElement);
46700         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46701         // this copies styles from the containing element into thsi one..
46702         // not sure why we need all of this..
46703         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46704         
46705         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46706         //ss['background-attachment'] = 'fixed'; // w3c
46707         dbody.bgProperties = 'fixed'; // ie
46708         //Roo.DomHelper.applyStyles(dbody, ss);
46709         Roo.EventManager.on(this.doc, {
46710             //'mousedown': this.onEditorEvent,
46711             'mouseup': this.onEditorEvent,
46712             'dblclick': this.onEditorEvent,
46713             'click': this.onEditorEvent,
46714             'keyup': this.onEditorEvent,
46715             
46716             buffer:100,
46717             scope: this
46718         });
46719         Roo.EventManager.on(this.doc, {
46720             'paste': this.onPasteEvent,
46721             scope : this
46722         });
46723         if(Roo.isGecko){
46724             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46725         }
46726         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46727             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46728         }
46729         this.initialized = true;
46730
46731         
46732         // initialize special key events - enter
46733         new Roo.htmleditor.KeyEnter({core : this});
46734         
46735          
46736         
46737         this.owner.fireEvent('initialize', this);
46738         this.pushValue();
46739     },
46740     
46741     onPasteEvent : function(e,v)
46742     {
46743         // I think we better assume paste is going to be a dirty load of rubish from word..
46744         
46745         // even pasting into a 'email version' of this widget will have to clean up that mess.
46746         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46747         
46748         // check what type of paste - if it's an image, then handle it differently.
46749         if (cd.files.length > 0) {
46750             // pasting images?
46751             var urlAPI = (window.createObjectURL && window) || 
46752                 (window.URL && URL.revokeObjectURL && URL) || 
46753                 (window.webkitURL && webkitURL);
46754     
46755             var url = urlAPI.createObjectURL( cd.files[0]);
46756             this.insertAtCursor('<img src=" + url + ">');
46757             return false;
46758         }
46759         
46760         var html = cd.getData('text/html'); // clipboard event
46761         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
46762         var images = parser.doc.getElementsByType('pict');
46763         Roo.log(images);
46764         //Roo.log(imgs);
46765         // fixme..
46766         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
46767                        .map(function(g) { return g.toDataURL(); });
46768         
46769         
46770         html = this.cleanWordChars(html);
46771         
46772         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46773         
46774         if (images.length > 0) {
46775             Roo.each(d.getElementsByTagName('img'), function(img, i) {
46776                 img.setAttribute('src', images[i]);
46777             });
46778         }
46779         
46780       
46781         new Roo.htmleditor.FilterStyleToTag({ node : d });
46782         new Roo.htmleditor.FilterAttributes({
46783             node : d,
46784             attrib_white : ['href', 'src', 'name', 'align'],
46785             attrib_clean : ['href', 'src' ] 
46786         });
46787         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46788         // should be fonts..
46789         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46790         new Roo.htmleditor.FilterParagraph({ node : d });
46791         new Roo.htmleditor.FilterSpan({ node : d });
46792         new Roo.htmleditor.FilterLongBr({ node : d });
46793         
46794         
46795         
46796         this.insertAtCursor(d.innerHTML);
46797         
46798         e.preventDefault();
46799         return false;
46800         // default behaveiour should be our local cleanup paste? (optional?)
46801         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46802         //this.owner.fireEvent('paste', e, v);
46803     },
46804     // private
46805     onDestroy : function(){
46806         
46807         
46808         
46809         if(this.rendered){
46810             
46811             //for (var i =0; i < this.toolbars.length;i++) {
46812             //    // fixme - ask toolbars for heights?
46813             //    this.toolbars[i].onDestroy();
46814            // }
46815             
46816             //this.wrap.dom.innerHTML = '';
46817             //this.wrap.remove();
46818         }
46819     },
46820
46821     // private
46822     onFirstFocus : function(){
46823         
46824         this.assignDocWin();
46825         
46826         
46827         this.activated = true;
46828          
46829     
46830         if(Roo.isGecko){ // prevent silly gecko errors
46831             this.win.focus();
46832             var s = this.win.getSelection();
46833             if(!s.focusNode || s.focusNode.nodeType != 3){
46834                 var r = s.getRangeAt(0);
46835                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46836                 r.collapse(true);
46837                 this.deferFocus();
46838             }
46839             try{
46840                 this.execCmd('useCSS', true);
46841                 this.execCmd('styleWithCSS', false);
46842             }catch(e){}
46843         }
46844         this.owner.fireEvent('activate', this);
46845     },
46846
46847     // private
46848     adjustFont: function(btn){
46849         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46850         //if(Roo.isSafari){ // safari
46851         //    adjust *= 2;
46852        // }
46853         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46854         if(Roo.isSafari){ // safari
46855             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46856             v =  (v < 10) ? 10 : v;
46857             v =  (v > 48) ? 48 : v;
46858             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46859             
46860         }
46861         
46862         
46863         v = Math.max(1, v+adjust);
46864         
46865         this.execCmd('FontSize', v  );
46866     },
46867
46868     onEditorEvent : function(e)
46869     {
46870         this.owner.fireEvent('editorevent', this, e);
46871       //  this.updateToolbar();
46872         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46873     },
46874
46875     insertTag : function(tg)
46876     {
46877         // could be a bit smarter... -> wrap the current selected tRoo..
46878         if (tg.toLowerCase() == 'span' ||
46879             tg.toLowerCase() == 'code' ||
46880             tg.toLowerCase() == 'sup' ||
46881             tg.toLowerCase() == 'sub' 
46882             ) {
46883             
46884             range = this.createRange(this.getSelection());
46885             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46886             wrappingNode.appendChild(range.extractContents());
46887             range.insertNode(wrappingNode);
46888
46889             return;
46890             
46891             
46892             
46893         }
46894         this.execCmd("formatblock",   tg);
46895         
46896     },
46897     
46898     insertText : function(txt)
46899     {
46900         
46901         
46902         var range = this.createRange();
46903         range.deleteContents();
46904                //alert(Sender.getAttribute('label'));
46905                
46906         range.insertNode(this.doc.createTextNode(txt));
46907     } ,
46908     
46909      
46910
46911     /**
46912      * Executes a Midas editor command on the editor document and performs necessary focus and
46913      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46914      * @param {String} cmd The Midas command
46915      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46916      */
46917     relayCmd : function(cmd, value){
46918         this.win.focus();
46919         this.execCmd(cmd, value);
46920         this.owner.fireEvent('editorevent', this);
46921         //this.updateToolbar();
46922         this.owner.deferFocus();
46923     },
46924
46925     /**
46926      * Executes a Midas editor command directly on the editor document.
46927      * For visual commands, you should use {@link #relayCmd} instead.
46928      * <b>This should only be called after the editor is initialized.</b>
46929      * @param {String} cmd The Midas command
46930      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46931      */
46932     execCmd : function(cmd, value){
46933         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46934         this.syncValue();
46935     },
46936  
46937  
46938    
46939     /**
46940      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46941      * to insert tRoo.
46942      * @param {String} text | dom node.. 
46943      */
46944     insertAtCursor : function(text)
46945     {
46946         
46947         if(!this.activated){
46948             return;
46949         }
46950          
46951         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46952             this.win.focus();
46953             
46954             
46955             // from jquery ui (MIT licenced)
46956             var range, node;
46957             var win = this.win;
46958             
46959             if (win.getSelection && win.getSelection().getRangeAt) {
46960                 
46961                 // delete the existing?
46962                 
46963                 this.createRange(this.getSelection()).deleteContents();
46964                 range = win.getSelection().getRangeAt(0);
46965                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46966                 range.insertNode(node);
46967             } else if (win.document.selection && win.document.selection.createRange) {
46968                 // no firefox support
46969                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46970                 win.document.selection.createRange().pasteHTML(txt);
46971             } else {
46972                 // no firefox support
46973                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46974                 this.execCmd('InsertHTML', txt);
46975             } 
46976             
46977             this.syncValue();
46978             
46979             this.deferFocus();
46980         }
46981     },
46982  // private
46983     mozKeyPress : function(e){
46984         if(e.ctrlKey){
46985             var c = e.getCharCode(), cmd;
46986           
46987             if(c > 0){
46988                 c = String.fromCharCode(c).toLowerCase();
46989                 switch(c){
46990                     case 'b':
46991                         cmd = 'bold';
46992                         break;
46993                     case 'i':
46994                         cmd = 'italic';
46995                         break;
46996                     
46997                     case 'u':
46998                         cmd = 'underline';
46999                         break;
47000                     
47001                     //case 'v':
47002                       //  this.cleanUpPaste.defer(100, this);
47003                       //  return;
47004                         
47005                 }
47006                 if(cmd){
47007                     this.win.focus();
47008                     this.execCmd(cmd);
47009                     this.deferFocus();
47010                     e.preventDefault();
47011                 }
47012                 
47013             }
47014         }
47015     },
47016
47017     // private
47018     fixKeys : function(){ // load time branching for fastest keydown performance
47019         if(Roo.isIE){
47020             return function(e){
47021                 var k = e.getKey(), r;
47022                 if(k == e.TAB){
47023                     e.stopEvent();
47024                     r = this.doc.selection.createRange();
47025                     if(r){
47026                         r.collapse(true);
47027                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47028                         this.deferFocus();
47029                     }
47030                     return;
47031                 }
47032                 
47033                 if(k == e.ENTER){
47034                     r = this.doc.selection.createRange();
47035                     if(r){
47036                         var target = r.parentElement();
47037                         if(!target || target.tagName.toLowerCase() != 'li'){
47038                             e.stopEvent();
47039                             r.pasteHTML('<br/>');
47040                             r.collapse(false);
47041                             r.select();
47042                         }
47043                     }
47044                 }
47045                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47046                 //    this.cleanUpPaste.defer(100, this);
47047                 //    return;
47048                 //}
47049                 
47050                 
47051             };
47052         }else if(Roo.isOpera){
47053             return function(e){
47054                 var k = e.getKey();
47055                 if(k == e.TAB){
47056                     e.stopEvent();
47057                     this.win.focus();
47058                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47059                     this.deferFocus();
47060                 }
47061                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47062                 //    this.cleanUpPaste.defer(100, this);
47063                  //   return;
47064                 //}
47065                 
47066             };
47067         }else if(Roo.isSafari){
47068             return function(e){
47069                 var k = e.getKey();
47070                 
47071                 if(k == e.TAB){
47072                     e.stopEvent();
47073                     this.execCmd('InsertText','\t');
47074                     this.deferFocus();
47075                     return;
47076                 }
47077                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47078                  //   this.cleanUpPaste.defer(100, this);
47079                  //   return;
47080                // }
47081                 
47082              };
47083         }
47084     }(),
47085     
47086     getAllAncestors: function()
47087     {
47088         var p = this.getSelectedNode();
47089         var a = [];
47090         if (!p) {
47091             a.push(p); // push blank onto stack..
47092             p = this.getParentElement();
47093         }
47094         
47095         
47096         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47097             a.push(p);
47098             p = p.parentNode;
47099         }
47100         a.push(this.doc.body);
47101         return a;
47102     },
47103     lastSel : false,
47104     lastSelNode : false,
47105     
47106     
47107     getSelection : function() 
47108     {
47109         this.assignDocWin();
47110         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47111     },
47112     /**
47113      * Select a dom node
47114      * @param {DomElement} node the node to select
47115      */
47116     selectNode : function(node)
47117     {
47118         
47119             var nodeRange = node.ownerDocument.createRange();
47120             try {
47121                 nodeRange.selectNode(node);
47122             } catch (e) {
47123                 nodeRange.selectNodeContents(node);
47124             }
47125             //nodeRange.collapse(true);
47126             var s = this.win.getSelection();
47127             s.removeAllRanges();
47128             s.addRange(nodeRange);
47129     },
47130     
47131     getSelectedNode: function() 
47132     {
47133         // this may only work on Gecko!!!
47134         
47135         // should we cache this!!!!
47136         
47137         
47138         
47139          
47140         var range = this.createRange(this.getSelection()).cloneRange();
47141         
47142         if (Roo.isIE) {
47143             var parent = range.parentElement();
47144             while (true) {
47145                 var testRange = range.duplicate();
47146                 testRange.moveToElementText(parent);
47147                 if (testRange.inRange(range)) {
47148                     break;
47149                 }
47150                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47151                     break;
47152                 }
47153                 parent = parent.parentElement;
47154             }
47155             return parent;
47156         }
47157         
47158         // is ancestor a text element.
47159         var ac =  range.commonAncestorContainer;
47160         if (ac.nodeType == 3) {
47161             ac = ac.parentNode;
47162         }
47163         
47164         var ar = ac.childNodes;
47165          
47166         var nodes = [];
47167         var other_nodes = [];
47168         var has_other_nodes = false;
47169         for (var i=0;i<ar.length;i++) {
47170             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47171                 continue;
47172             }
47173             // fullly contained node.
47174             
47175             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47176                 nodes.push(ar[i]);
47177                 continue;
47178             }
47179             
47180             // probably selected..
47181             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47182                 other_nodes.push(ar[i]);
47183                 continue;
47184             }
47185             // outer..
47186             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47187                 continue;
47188             }
47189             
47190             
47191             has_other_nodes = true;
47192         }
47193         if (!nodes.length && other_nodes.length) {
47194             nodes= other_nodes;
47195         }
47196         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47197             return false;
47198         }
47199         
47200         return nodes[0];
47201     },
47202     createRange: function(sel)
47203     {
47204         // this has strange effects when using with 
47205         // top toolbar - not sure if it's a great idea.
47206         //this.editor.contentWindow.focus();
47207         if (typeof sel != "undefined") {
47208             try {
47209                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47210             } catch(e) {
47211                 return this.doc.createRange();
47212             }
47213         } else {
47214             return this.doc.createRange();
47215         }
47216     },
47217     getParentElement: function()
47218     {
47219         
47220         this.assignDocWin();
47221         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47222         
47223         var range = this.createRange(sel);
47224          
47225         try {
47226             var p = range.commonAncestorContainer;
47227             while (p.nodeType == 3) { // text node
47228                 p = p.parentNode;
47229             }
47230             return p;
47231         } catch (e) {
47232             return null;
47233         }
47234     
47235     },
47236     /***
47237      *
47238      * Range intersection.. the hard stuff...
47239      *  '-1' = before
47240      *  '0' = hits..
47241      *  '1' = after.
47242      *         [ -- selected range --- ]
47243      *   [fail]                        [fail]
47244      *
47245      *    basically..
47246      *      if end is before start or  hits it. fail.
47247      *      if start is after end or hits it fail.
47248      *
47249      *   if either hits (but other is outside. - then it's not 
47250      *   
47251      *    
47252      **/
47253     
47254     
47255     // @see http://www.thismuchiknow.co.uk/?p=64.
47256     rangeIntersectsNode : function(range, node)
47257     {
47258         var nodeRange = node.ownerDocument.createRange();
47259         try {
47260             nodeRange.selectNode(node);
47261         } catch (e) {
47262             nodeRange.selectNodeContents(node);
47263         }
47264     
47265         var rangeStartRange = range.cloneRange();
47266         rangeStartRange.collapse(true);
47267     
47268         var rangeEndRange = range.cloneRange();
47269         rangeEndRange.collapse(false);
47270     
47271         var nodeStartRange = nodeRange.cloneRange();
47272         nodeStartRange.collapse(true);
47273     
47274         var nodeEndRange = nodeRange.cloneRange();
47275         nodeEndRange.collapse(false);
47276     
47277         return rangeStartRange.compareBoundaryPoints(
47278                  Range.START_TO_START, nodeEndRange) == -1 &&
47279                rangeEndRange.compareBoundaryPoints(
47280                  Range.START_TO_START, nodeStartRange) == 1;
47281         
47282          
47283     },
47284     rangeCompareNode : function(range, node)
47285     {
47286         var nodeRange = node.ownerDocument.createRange();
47287         try {
47288             nodeRange.selectNode(node);
47289         } catch (e) {
47290             nodeRange.selectNodeContents(node);
47291         }
47292         
47293         
47294         range.collapse(true);
47295     
47296         nodeRange.collapse(true);
47297      
47298         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47299         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47300          
47301         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47302         
47303         var nodeIsBefore   =  ss == 1;
47304         var nodeIsAfter    = ee == -1;
47305         
47306         if (nodeIsBefore && nodeIsAfter) {
47307             return 0; // outer
47308         }
47309         if (!nodeIsBefore && nodeIsAfter) {
47310             return 1; //right trailed.
47311         }
47312         
47313         if (nodeIsBefore && !nodeIsAfter) {
47314             return 2;  // left trailed.
47315         }
47316         // fully contined.
47317         return 3;
47318     },
47319  
47320     cleanWordChars : function(input) {// change the chars to hex code
47321         
47322        var swapCodes  = [ 
47323             [    8211, "&#8211;" ], 
47324             [    8212, "&#8212;" ], 
47325             [    8216,  "'" ],  
47326             [    8217, "'" ],  
47327             [    8220, '"' ],  
47328             [    8221, '"' ],  
47329             [    8226, "*" ],  
47330             [    8230, "..." ]
47331         ]; 
47332         var output = input;
47333         Roo.each(swapCodes, function(sw) { 
47334             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47335             
47336             output = output.replace(swapper, sw[1]);
47337         });
47338         
47339         return output;
47340     },
47341     
47342      
47343     
47344         
47345     
47346     cleanUpChild : function (node)
47347     {
47348         
47349         new Roo.htmleditor.FilterComment({node : node});
47350         new Roo.htmleditor.FilterAttributes({
47351                 node : node,
47352                 attrib_black : this.ablack,
47353                 attrib_clean : this.aclean,
47354                 style_white : this.cwhite,
47355                 style_black : this.cblack
47356         });
47357         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47358         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47359          
47360         
47361     },
47362     
47363     /**
47364      * Clean up MS wordisms...
47365      * @deprecated - use filter directly
47366      */
47367     cleanWord : function(node)
47368     {
47369         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47370         
47371     },
47372    
47373     
47374     /**
47375
47376      * @deprecated - use filters
47377      */
47378     cleanTableWidths : function(node)
47379     {
47380         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47381         
47382  
47383     },
47384     
47385      
47386         
47387     applyBlacklists : function()
47388     {
47389         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47390         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47391         
47392         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47393         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47394         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47395         
47396         this.white = [];
47397         this.black = [];
47398         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47399             if (b.indexOf(tag) > -1) {
47400                 return;
47401             }
47402             this.white.push(tag);
47403             
47404         }, this);
47405         
47406         Roo.each(w, function(tag) {
47407             if (b.indexOf(tag) > -1) {
47408                 return;
47409             }
47410             if (this.white.indexOf(tag) > -1) {
47411                 return;
47412             }
47413             this.white.push(tag);
47414             
47415         }, this);
47416         
47417         
47418         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47419             if (w.indexOf(tag) > -1) {
47420                 return;
47421             }
47422             this.black.push(tag);
47423             
47424         }, this);
47425         
47426         Roo.each(b, function(tag) {
47427             if (w.indexOf(tag) > -1) {
47428                 return;
47429             }
47430             if (this.black.indexOf(tag) > -1) {
47431                 return;
47432             }
47433             this.black.push(tag);
47434             
47435         }, this);
47436         
47437         
47438         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47439         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47440         
47441         this.cwhite = [];
47442         this.cblack = [];
47443         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47444             if (b.indexOf(tag) > -1) {
47445                 return;
47446             }
47447             this.cwhite.push(tag);
47448             
47449         }, this);
47450         
47451         Roo.each(w, function(tag) {
47452             if (b.indexOf(tag) > -1) {
47453                 return;
47454             }
47455             if (this.cwhite.indexOf(tag) > -1) {
47456                 return;
47457             }
47458             this.cwhite.push(tag);
47459             
47460         }, this);
47461         
47462         
47463         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47464             if (w.indexOf(tag) > -1) {
47465                 return;
47466             }
47467             this.cblack.push(tag);
47468             
47469         }, this);
47470         
47471         Roo.each(b, function(tag) {
47472             if (w.indexOf(tag) > -1) {
47473                 return;
47474             }
47475             if (this.cblack.indexOf(tag) > -1) {
47476                 return;
47477             }
47478             this.cblack.push(tag);
47479             
47480         }, this);
47481     },
47482     
47483     setStylesheets : function(stylesheets)
47484     {
47485         if(typeof(stylesheets) == 'string'){
47486             Roo.get(this.iframe.contentDocument.head).createChild({
47487                 tag : 'link',
47488                 rel : 'stylesheet',
47489                 type : 'text/css',
47490                 href : stylesheets
47491             });
47492             
47493             return;
47494         }
47495         var _this = this;
47496      
47497         Roo.each(stylesheets, function(s) {
47498             if(!s.length){
47499                 return;
47500             }
47501             
47502             Roo.get(_this.iframe.contentDocument.head).createChild({
47503                 tag : 'link',
47504                 rel : 'stylesheet',
47505                 type : 'text/css',
47506                 href : s
47507             });
47508         });
47509
47510         
47511     },
47512     
47513     removeStylesheets : function()
47514     {
47515         var _this = this;
47516         
47517         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47518             s.remove();
47519         });
47520     },
47521     
47522     setStyle : function(style)
47523     {
47524         Roo.get(this.iframe.contentDocument.head).createChild({
47525             tag : 'style',
47526             type : 'text/css',
47527             html : style
47528         });
47529
47530         return;
47531     }
47532     
47533     // hide stuff that is not compatible
47534     /**
47535      * @event blur
47536      * @hide
47537      */
47538     /**
47539      * @event change
47540      * @hide
47541      */
47542     /**
47543      * @event focus
47544      * @hide
47545      */
47546     /**
47547      * @event specialkey
47548      * @hide
47549      */
47550     /**
47551      * @cfg {String} fieldClass @hide
47552      */
47553     /**
47554      * @cfg {String} focusClass @hide
47555      */
47556     /**
47557      * @cfg {String} autoCreate @hide
47558      */
47559     /**
47560      * @cfg {String} inputType @hide
47561      */
47562     /**
47563      * @cfg {String} invalidClass @hide
47564      */
47565     /**
47566      * @cfg {String} invalidText @hide
47567      */
47568     /**
47569      * @cfg {String} msgFx @hide
47570      */
47571     /**
47572      * @cfg {String} validateOnBlur @hide
47573      */
47574 });
47575
47576 Roo.HtmlEditorCore.white = [
47577         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47578         
47579        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47580        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47581        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47582        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47583        'TABLE',   'UL',         'XMP', 
47584        
47585        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47586       'THEAD',   'TR', 
47587      
47588       'DIR', 'MENU', 'OL', 'UL', 'DL',
47589        
47590       'EMBED',  'OBJECT'
47591 ];
47592
47593
47594 Roo.HtmlEditorCore.black = [
47595     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47596         'APPLET', // 
47597         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47598         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47599         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47600         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47601         //'FONT' // CLEAN LATER..
47602         'COLGROUP', 'COL'  // messy tables.
47603         
47604 ];
47605 Roo.HtmlEditorCore.clean = [ // ?? needed???
47606      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47607 ];
47608 Roo.HtmlEditorCore.tag_remove = [
47609     'FONT', 'TBODY'  
47610 ];
47611 // attributes..
47612
47613 Roo.HtmlEditorCore.ablack = [
47614     'on'
47615 ];
47616     
47617 Roo.HtmlEditorCore.aclean = [ 
47618     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47619 ];
47620
47621 // protocols..
47622 Roo.HtmlEditorCore.pwhite= [
47623         'http',  'https',  'mailto'
47624 ];
47625
47626 // white listed style attributes.
47627 Roo.HtmlEditorCore.cwhite= [
47628       //  'text-align', /// default is to allow most things..
47629       
47630          
47631 //        'font-size'//??
47632 ];
47633
47634 // black listed style attributes.
47635 Roo.HtmlEditorCore.cblack= [
47636       //  'font-size' -- this can be set by the project 
47637 ];
47638
47639
47640
47641
47642     //<script type="text/javascript">
47643
47644 /*
47645  * Ext JS Library 1.1.1
47646  * Copyright(c) 2006-2007, Ext JS, LLC.
47647  * Licence LGPL
47648  * 
47649  */
47650  
47651  
47652 Roo.form.HtmlEditor = function(config){
47653     
47654     
47655     
47656     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47657     
47658     if (!this.toolbars) {
47659         this.toolbars = [];
47660     }
47661     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47662     
47663     
47664 };
47665
47666 /**
47667  * @class Roo.form.HtmlEditor
47668  * @extends Roo.form.Field
47669  * Provides a lightweight HTML Editor component.
47670  *
47671  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47672  * 
47673  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47674  * supported by this editor.</b><br/><br/>
47675  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47676  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47677  */
47678 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47679     /**
47680      * @cfg {Boolean} clearUp
47681      */
47682     clearUp : true,
47683       /**
47684      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47685      */
47686     toolbars : false,
47687    
47688      /**
47689      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47690      *                        Roo.resizable.
47691      */
47692     resizable : false,
47693      /**
47694      * @cfg {Number} height (in pixels)
47695      */   
47696     height: 300,
47697    /**
47698      * @cfg {Number} width (in pixels)
47699      */   
47700     width: 500,
47701     
47702     /**
47703      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47704      * 
47705      */
47706     stylesheets: false,
47707     
47708     
47709      /**
47710      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47711      * 
47712      */
47713     cblack: false,
47714     /**
47715      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47716      * 
47717      */
47718     cwhite: false,
47719     
47720      /**
47721      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47722      * 
47723      */
47724     black: false,
47725     /**
47726      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47727      * 
47728      */
47729     white: false,
47730     /**
47731      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47732      */
47733     allowComments: false,
47734     /**
47735      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47736      */
47737     
47738     
47739      bodyCls : '',
47740     
47741     // id of frame..
47742     frameId: false,
47743     
47744     // private properties
47745     validationEvent : false,
47746     deferHeight: true,
47747     initialized : false,
47748     activated : false,
47749     
47750     onFocus : Roo.emptyFn,
47751     iframePad:3,
47752     hideMode:'offsets',
47753     
47754     actionMode : 'container', // defaults to hiding it...
47755     
47756     defaultAutoCreate : { // modified by initCompnoent..
47757         tag: "textarea",
47758         style:"width:500px;height:300px;",
47759         autocomplete: "new-password"
47760     },
47761
47762     // private
47763     initComponent : function(){
47764         this.addEvents({
47765             /**
47766              * @event initialize
47767              * Fires when the editor is fully initialized (including the iframe)
47768              * @param {HtmlEditor} this
47769              */
47770             initialize: true,
47771             /**
47772              * @event activate
47773              * Fires when the editor is first receives the focus. Any insertion must wait
47774              * until after this event.
47775              * @param {HtmlEditor} this
47776              */
47777             activate: true,
47778              /**
47779              * @event beforesync
47780              * Fires before the textarea is updated with content from the editor iframe. Return false
47781              * to cancel the sync.
47782              * @param {HtmlEditor} this
47783              * @param {String} html
47784              */
47785             beforesync: true,
47786              /**
47787              * @event beforepush
47788              * Fires before the iframe editor is updated with content from the textarea. Return false
47789              * to cancel the push.
47790              * @param {HtmlEditor} this
47791              * @param {String} html
47792              */
47793             beforepush: true,
47794              /**
47795              * @event sync
47796              * Fires when the textarea is updated with content from the editor iframe.
47797              * @param {HtmlEditor} this
47798              * @param {String} html
47799              */
47800             sync: true,
47801              /**
47802              * @event push
47803              * Fires when the iframe editor is updated with content from the textarea.
47804              * @param {HtmlEditor} this
47805              * @param {String} html
47806              */
47807             push: true,
47808              /**
47809              * @event editmodechange
47810              * Fires when the editor switches edit modes
47811              * @param {HtmlEditor} this
47812              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47813              */
47814             editmodechange: true,
47815             /**
47816              * @event editorevent
47817              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47818              * @param {HtmlEditor} this
47819              */
47820             editorevent: true,
47821             /**
47822              * @event firstfocus
47823              * Fires when on first focus - needed by toolbars..
47824              * @param {HtmlEditor} this
47825              */
47826             firstfocus: true,
47827             /**
47828              * @event autosave
47829              * Auto save the htmlEditor value as a file into Events
47830              * @param {HtmlEditor} this
47831              */
47832             autosave: true,
47833             /**
47834              * @event savedpreview
47835              * preview the saved version of htmlEditor
47836              * @param {HtmlEditor} this
47837              */
47838             savedpreview: true,
47839             
47840             /**
47841             * @event stylesheetsclick
47842             * Fires when press the Sytlesheets button
47843             * @param {Roo.HtmlEditorCore} this
47844             */
47845             stylesheetsclick: true,
47846             /**
47847             * @event paste
47848             * Fires when press user pastes into the editor
47849             * @param {Roo.HtmlEditorCore} this
47850             */
47851             paste: true 
47852         });
47853         this.defaultAutoCreate =  {
47854             tag: "textarea",
47855             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47856             autocomplete: "new-password"
47857         };
47858     },
47859
47860     /**
47861      * Protected method that will not generally be called directly. It
47862      * is called when the editor creates its toolbar. Override this method if you need to
47863      * add custom toolbar buttons.
47864      * @param {HtmlEditor} editor
47865      */
47866     createToolbar : function(editor){
47867         Roo.log("create toolbars");
47868         if (!editor.toolbars || !editor.toolbars.length) {
47869             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47870         }
47871         
47872         for (var i =0 ; i < editor.toolbars.length;i++) {
47873             editor.toolbars[i] = Roo.factory(
47874                     typeof(editor.toolbars[i]) == 'string' ?
47875                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47876                 Roo.form.HtmlEditor);
47877             editor.toolbars[i].init(editor);
47878         }
47879          
47880         
47881     },
47882
47883      
47884     // private
47885     onRender : function(ct, position)
47886     {
47887         var _t = this;
47888         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47889         
47890         this.wrap = this.el.wrap({
47891             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47892         });
47893         
47894         this.editorcore.onRender(ct, position);
47895          
47896         if (this.resizable) {
47897             this.resizeEl = new Roo.Resizable(this.wrap, {
47898                 pinned : true,
47899                 wrap: true,
47900                 dynamic : true,
47901                 minHeight : this.height,
47902                 height: this.height,
47903                 handles : this.resizable,
47904                 width: this.width,
47905                 listeners : {
47906                     resize : function(r, w, h) {
47907                         _t.onResize(w,h); // -something
47908                     }
47909                 }
47910             });
47911             
47912         }
47913         this.createToolbar(this);
47914        
47915         
47916         if(!this.width){
47917             this.setSize(this.wrap.getSize());
47918         }
47919         if (this.resizeEl) {
47920             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47921             // should trigger onReize..
47922         }
47923         
47924         this.keyNav = new Roo.KeyNav(this.el, {
47925             
47926             "tab" : function(e){
47927                 e.preventDefault();
47928                 
47929                 var value = this.getValue();
47930                 
47931                 var start = this.el.dom.selectionStart;
47932                 var end = this.el.dom.selectionEnd;
47933                 
47934                 if(!e.shiftKey){
47935                     
47936                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47937                     this.el.dom.setSelectionRange(end + 1, end + 1);
47938                     return;
47939                 }
47940                 
47941                 var f = value.substring(0, start).split("\t");
47942                 
47943                 if(f.pop().length != 0){
47944                     return;
47945                 }
47946                 
47947                 this.setValue(f.join("\t") + value.substring(end));
47948                 this.el.dom.setSelectionRange(start - 1, start - 1);
47949                 
47950             },
47951             
47952             "home" : function(e){
47953                 e.preventDefault();
47954                 
47955                 var curr = this.el.dom.selectionStart;
47956                 var lines = this.getValue().split("\n");
47957                 
47958                 if(!lines.length){
47959                     return;
47960                 }
47961                 
47962                 if(e.ctrlKey){
47963                     this.el.dom.setSelectionRange(0, 0);
47964                     return;
47965                 }
47966                 
47967                 var pos = 0;
47968                 
47969                 for (var i = 0; i < lines.length;i++) {
47970                     pos += lines[i].length;
47971                     
47972                     if(i != 0){
47973                         pos += 1;
47974                     }
47975                     
47976                     if(pos < curr){
47977                         continue;
47978                     }
47979                     
47980                     pos -= lines[i].length;
47981                     
47982                     break;
47983                 }
47984                 
47985                 if(!e.shiftKey){
47986                     this.el.dom.setSelectionRange(pos, pos);
47987                     return;
47988                 }
47989                 
47990                 this.el.dom.selectionStart = pos;
47991                 this.el.dom.selectionEnd = curr;
47992             },
47993             
47994             "end" : function(e){
47995                 e.preventDefault();
47996                 
47997                 var curr = this.el.dom.selectionStart;
47998                 var lines = this.getValue().split("\n");
47999                 
48000                 if(!lines.length){
48001                     return;
48002                 }
48003                 
48004                 if(e.ctrlKey){
48005                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48006                     return;
48007                 }
48008                 
48009                 var pos = 0;
48010                 
48011                 for (var i = 0; i < lines.length;i++) {
48012                     
48013                     pos += lines[i].length;
48014                     
48015                     if(i != 0){
48016                         pos += 1;
48017                     }
48018                     
48019                     if(pos < curr){
48020                         continue;
48021                     }
48022                     
48023                     break;
48024                 }
48025                 
48026                 if(!e.shiftKey){
48027                     this.el.dom.setSelectionRange(pos, pos);
48028                     return;
48029                 }
48030                 
48031                 this.el.dom.selectionStart = curr;
48032                 this.el.dom.selectionEnd = pos;
48033             },
48034
48035             scope : this,
48036
48037             doRelay : function(foo, bar, hname){
48038                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48039             },
48040
48041             forceKeyDown: true
48042         });
48043         
48044 //        if(this.autosave && this.w){
48045 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48046 //        }
48047     },
48048
48049     // private
48050     onResize : function(w, h)
48051     {
48052         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48053         var ew = false;
48054         var eh = false;
48055         
48056         if(this.el ){
48057             if(typeof w == 'number'){
48058                 var aw = w - this.wrap.getFrameWidth('lr');
48059                 this.el.setWidth(this.adjustWidth('textarea', aw));
48060                 ew = aw;
48061             }
48062             if(typeof h == 'number'){
48063                 var tbh = 0;
48064                 for (var i =0; i < this.toolbars.length;i++) {
48065                     // fixme - ask toolbars for heights?
48066                     tbh += this.toolbars[i].tb.el.getHeight();
48067                     if (this.toolbars[i].footer) {
48068                         tbh += this.toolbars[i].footer.el.getHeight();
48069                     }
48070                 }
48071                 
48072                 
48073                 
48074                 
48075                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48076                 ah -= 5; // knock a few pixes off for look..
48077 //                Roo.log(ah);
48078                 this.el.setHeight(this.adjustWidth('textarea', ah));
48079                 var eh = ah;
48080             }
48081         }
48082         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48083         this.editorcore.onResize(ew,eh);
48084         
48085     },
48086
48087     /**
48088      * Toggles the editor between standard and source edit mode.
48089      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48090      */
48091     toggleSourceEdit : function(sourceEditMode)
48092     {
48093         this.editorcore.toggleSourceEdit(sourceEditMode);
48094         
48095         if(this.editorcore.sourceEditMode){
48096             Roo.log('editor - showing textarea');
48097             
48098 //            Roo.log('in');
48099 //            Roo.log(this.syncValue());
48100             this.editorcore.syncValue();
48101             this.el.removeClass('x-hidden');
48102             this.el.dom.removeAttribute('tabIndex');
48103             this.el.focus();
48104             this.el.dom.scrollTop = 0;
48105             
48106             
48107             for (var i = 0; i < this.toolbars.length; i++) {
48108                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48109                     this.toolbars[i].tb.hide();
48110                     this.toolbars[i].footer.hide();
48111                 }
48112             }
48113             
48114         }else{
48115             Roo.log('editor - hiding textarea');
48116 //            Roo.log('out')
48117 //            Roo.log(this.pushValue()); 
48118             this.editorcore.pushValue();
48119             
48120             this.el.addClass('x-hidden');
48121             this.el.dom.setAttribute('tabIndex', -1);
48122             
48123             for (var i = 0; i < this.toolbars.length; i++) {
48124                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48125                     this.toolbars[i].tb.show();
48126                     this.toolbars[i].footer.show();
48127                 }
48128             }
48129             
48130             //this.deferFocus();
48131         }
48132         
48133         this.setSize(this.wrap.getSize());
48134         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48135         
48136         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48137     },
48138  
48139     // private (for BoxComponent)
48140     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48141
48142     // private (for BoxComponent)
48143     getResizeEl : function(){
48144         return this.wrap;
48145     },
48146
48147     // private (for BoxComponent)
48148     getPositionEl : function(){
48149         return this.wrap;
48150     },
48151
48152     // private
48153     initEvents : function(){
48154         this.originalValue = this.getValue();
48155     },
48156
48157     /**
48158      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48159      * @method
48160      */
48161     markInvalid : Roo.emptyFn,
48162     /**
48163      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48164      * @method
48165      */
48166     clearInvalid : Roo.emptyFn,
48167
48168     setValue : function(v){
48169         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48170         this.editorcore.pushValue();
48171     },
48172
48173      
48174     // private
48175     deferFocus : function(){
48176         this.focus.defer(10, this);
48177     },
48178
48179     // doc'ed in Field
48180     focus : function(){
48181         this.editorcore.focus();
48182         
48183     },
48184       
48185
48186     // private
48187     onDestroy : function(){
48188         
48189         
48190         
48191         if(this.rendered){
48192             
48193             for (var i =0; i < this.toolbars.length;i++) {
48194                 // fixme - ask toolbars for heights?
48195                 this.toolbars[i].onDestroy();
48196             }
48197             
48198             this.wrap.dom.innerHTML = '';
48199             this.wrap.remove();
48200         }
48201     },
48202
48203     // private
48204     onFirstFocus : function(){
48205         //Roo.log("onFirstFocus");
48206         this.editorcore.onFirstFocus();
48207          for (var i =0; i < this.toolbars.length;i++) {
48208             this.toolbars[i].onFirstFocus();
48209         }
48210         
48211     },
48212     
48213     // private
48214     syncValue : function()
48215     {
48216         this.editorcore.syncValue();
48217     },
48218     
48219     pushValue : function()
48220     {
48221         this.editorcore.pushValue();
48222     },
48223     
48224     setStylesheets : function(stylesheets)
48225     {
48226         this.editorcore.setStylesheets(stylesheets);
48227     },
48228     
48229     removeStylesheets : function()
48230     {
48231         this.editorcore.removeStylesheets();
48232     }
48233      
48234     
48235     // hide stuff that is not compatible
48236     /**
48237      * @event blur
48238      * @hide
48239      */
48240     /**
48241      * @event change
48242      * @hide
48243      */
48244     /**
48245      * @event focus
48246      * @hide
48247      */
48248     /**
48249      * @event specialkey
48250      * @hide
48251      */
48252     /**
48253      * @cfg {String} fieldClass @hide
48254      */
48255     /**
48256      * @cfg {String} focusClass @hide
48257      */
48258     /**
48259      * @cfg {String} autoCreate @hide
48260      */
48261     /**
48262      * @cfg {String} inputType @hide
48263      */
48264     /**
48265      * @cfg {String} invalidClass @hide
48266      */
48267     /**
48268      * @cfg {String} invalidText @hide
48269      */
48270     /**
48271      * @cfg {String} msgFx @hide
48272      */
48273     /**
48274      * @cfg {String} validateOnBlur @hide
48275      */
48276 });
48277  
48278     // <script type="text/javascript">
48279 /*
48280  * Based on
48281  * Ext JS Library 1.1.1
48282  * Copyright(c) 2006-2007, Ext JS, LLC.
48283  *  
48284  
48285  */
48286
48287 /**
48288  * @class Roo.form.HtmlEditorToolbar1
48289  * Basic Toolbar
48290  * 
48291  * Usage:
48292  *
48293  new Roo.form.HtmlEditor({
48294     ....
48295     toolbars : [
48296         new Roo.form.HtmlEditorToolbar1({
48297             disable : { fonts: 1 , format: 1, ..., ... , ...],
48298             btns : [ .... ]
48299         })
48300     }
48301      
48302  * 
48303  * @cfg {Object} disable List of elements to disable..
48304  * @cfg {Array} btns List of additional buttons.
48305  * 
48306  * 
48307  * NEEDS Extra CSS? 
48308  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48309  */
48310  
48311 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48312 {
48313     
48314     Roo.apply(this, config);
48315     
48316     // default disabled, based on 'good practice'..
48317     this.disable = this.disable || {};
48318     Roo.applyIf(this.disable, {
48319         fontSize : true,
48320         colors : true,
48321         specialElements : true
48322     });
48323     
48324     
48325     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48326     // dont call parent... till later.
48327 }
48328
48329 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48330     
48331     tb: false,
48332     
48333     rendered: false,
48334     
48335     editor : false,
48336     editorcore : false,
48337     /**
48338      * @cfg {Object} disable  List of toolbar elements to disable
48339          
48340      */
48341     disable : false,
48342     
48343     
48344      /**
48345      * @cfg {String} createLinkText The default text for the create link prompt
48346      */
48347     createLinkText : 'Please enter the URL for the link:',
48348     /**
48349      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48350      */
48351     defaultLinkValue : 'http:/'+'/',
48352    
48353     
48354       /**
48355      * @cfg {Array} fontFamilies An array of available font families
48356      */
48357     fontFamilies : [
48358         'Arial',
48359         'Courier New',
48360         'Tahoma',
48361         'Times New Roman',
48362         'Verdana'
48363     ],
48364     
48365     specialChars : [
48366            "&#169;",
48367           "&#174;",     
48368           "&#8482;",    
48369           "&#163;" ,    
48370          // "&#8212;",    
48371           "&#8230;",    
48372           "&#247;" ,    
48373         //  "&#225;" ,     ?? a acute?
48374            "&#8364;"    , //Euro
48375        //   "&#8220;"    ,
48376         //  "&#8221;"    ,
48377         //  "&#8226;"    ,
48378           "&#176;"  //   , // degrees
48379
48380          // "&#233;"     , // e ecute
48381          // "&#250;"     , // u ecute?
48382     ],
48383     
48384     specialElements : [
48385         {
48386             text: "Insert Table",
48387             xtype: 'MenuItem',
48388             xns : Roo.Menu,
48389             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48390                 
48391         },
48392         {    
48393             text: "Insert Image",
48394             xtype: 'MenuItem',
48395             xns : Roo.Menu,
48396             ihtml : '<img src="about:blank"/>'
48397             
48398         }
48399         
48400          
48401     ],
48402     
48403     
48404     inputElements : [ 
48405             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48406             "input:submit", "input:button", "select", "textarea", "label" ],
48407     formats : [
48408         ["p"] ,  
48409         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48410         ["pre"],[ "code"], 
48411         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48412         ['div'],['span'],
48413         ['sup'],['sub']
48414     ],
48415     
48416     cleanStyles : [
48417         "font-size"
48418     ],
48419      /**
48420      * @cfg {String} defaultFont default font to use.
48421      */
48422     defaultFont: 'tahoma',
48423    
48424     fontSelect : false,
48425     
48426     
48427     formatCombo : false,
48428     
48429     init : function(editor)
48430     {
48431         this.editor = editor;
48432         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48433         var editorcore = this.editorcore;
48434         
48435         var _t = this;
48436         
48437         var fid = editorcore.frameId;
48438         var etb = this;
48439         function btn(id, toggle, handler){
48440             var xid = fid + '-'+ id ;
48441             return {
48442                 id : xid,
48443                 cmd : id,
48444                 cls : 'x-btn-icon x-edit-'+id,
48445                 enableToggle:toggle !== false,
48446                 scope: _t, // was editor...
48447                 handler:handler||_t.relayBtnCmd,
48448                 clickEvent:'mousedown',
48449                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48450                 tabIndex:-1
48451             };
48452         }
48453         
48454         
48455         
48456         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48457         this.tb = tb;
48458          // stop form submits
48459         tb.el.on('click', function(e){
48460             e.preventDefault(); // what does this do?
48461         });
48462
48463         if(!this.disable.font) { // && !Roo.isSafari){
48464             /* why no safari for fonts 
48465             editor.fontSelect = tb.el.createChild({
48466                 tag:'select',
48467                 tabIndex: -1,
48468                 cls:'x-font-select',
48469                 html: this.createFontOptions()
48470             });
48471             
48472             editor.fontSelect.on('change', function(){
48473                 var font = editor.fontSelect.dom.value;
48474                 editor.relayCmd('fontname', font);
48475                 editor.deferFocus();
48476             }, editor);
48477             
48478             tb.add(
48479                 editor.fontSelect.dom,
48480                 '-'
48481             );
48482             */
48483             
48484         };
48485         if(!this.disable.formats){
48486             this.formatCombo = new Roo.form.ComboBox({
48487                 store: new Roo.data.SimpleStore({
48488                     id : 'tag',
48489                     fields: ['tag'],
48490                     data : this.formats // from states.js
48491                 }),
48492                 blockFocus : true,
48493                 name : '',
48494                 //autoCreate : {tag: "div",  size: "20"},
48495                 displayField:'tag',
48496                 typeAhead: false,
48497                 mode: 'local',
48498                 editable : false,
48499                 triggerAction: 'all',
48500                 emptyText:'Add tag',
48501                 selectOnFocus:true,
48502                 width:135,
48503                 listeners : {
48504                     'select': function(c, r, i) {
48505                         editorcore.insertTag(r.get('tag'));
48506                         editor.focus();
48507                     }
48508                 }
48509
48510             });
48511             tb.addField(this.formatCombo);
48512             
48513         }
48514         
48515         if(!this.disable.format){
48516             tb.add(
48517                 btn('bold'),
48518                 btn('italic'),
48519                 btn('underline'),
48520                 btn('strikethrough')
48521             );
48522         };
48523         if(!this.disable.fontSize){
48524             tb.add(
48525                 '-',
48526                 
48527                 
48528                 btn('increasefontsize', false, editorcore.adjustFont),
48529                 btn('decreasefontsize', false, editorcore.adjustFont)
48530             );
48531         };
48532         
48533         
48534         if(!this.disable.colors){
48535             tb.add(
48536                 '-', {
48537                     id:editorcore.frameId +'-forecolor',
48538                     cls:'x-btn-icon x-edit-forecolor',
48539                     clickEvent:'mousedown',
48540                     tooltip: this.buttonTips['forecolor'] || undefined,
48541                     tabIndex:-1,
48542                     menu : new Roo.menu.ColorMenu({
48543                         allowReselect: true,
48544                         focus: Roo.emptyFn,
48545                         value:'000000',
48546                         plain:true,
48547                         selectHandler: function(cp, color){
48548                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48549                             editor.deferFocus();
48550                         },
48551                         scope: editorcore,
48552                         clickEvent:'mousedown'
48553                     })
48554                 }, {
48555                     id:editorcore.frameId +'backcolor',
48556                     cls:'x-btn-icon x-edit-backcolor',
48557                     clickEvent:'mousedown',
48558                     tooltip: this.buttonTips['backcolor'] || undefined,
48559                     tabIndex:-1,
48560                     menu : new Roo.menu.ColorMenu({
48561                         focus: Roo.emptyFn,
48562                         value:'FFFFFF',
48563                         plain:true,
48564                         allowReselect: true,
48565                         selectHandler: function(cp, color){
48566                             if(Roo.isGecko){
48567                                 editorcore.execCmd('useCSS', false);
48568                                 editorcore.execCmd('hilitecolor', color);
48569                                 editorcore.execCmd('useCSS', true);
48570                                 editor.deferFocus();
48571                             }else{
48572                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48573                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48574                                 editor.deferFocus();
48575                             }
48576                         },
48577                         scope:editorcore,
48578                         clickEvent:'mousedown'
48579                     })
48580                 }
48581             );
48582         };
48583         // now add all the items...
48584         
48585
48586         if(!this.disable.alignments){
48587             tb.add(
48588                 '-',
48589                 btn('justifyleft'),
48590                 btn('justifycenter'),
48591                 btn('justifyright')
48592             );
48593         };
48594
48595         //if(!Roo.isSafari){
48596             if(!this.disable.links){
48597                 tb.add(
48598                     '-',
48599                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48600                 );
48601             };
48602
48603             if(!this.disable.lists){
48604                 tb.add(
48605                     '-',
48606                     btn('insertorderedlist'),
48607                     btn('insertunorderedlist')
48608                 );
48609             }
48610             if(!this.disable.sourceEdit){
48611                 tb.add(
48612                     '-',
48613                     btn('sourceedit', true, function(btn){
48614                         this.toggleSourceEdit(btn.pressed);
48615                     })
48616                 );
48617             }
48618         //}
48619         
48620         var smenu = { };
48621         // special menu.. - needs to be tidied up..
48622         if (!this.disable.special) {
48623             smenu = {
48624                 text: "&#169;",
48625                 cls: 'x-edit-none',
48626                 
48627                 menu : {
48628                     items : []
48629                 }
48630             };
48631             for (var i =0; i < this.specialChars.length; i++) {
48632                 smenu.menu.items.push({
48633                     
48634                     html: this.specialChars[i],
48635                     handler: function(a,b) {
48636                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48637                         //editor.insertAtCursor(a.html);
48638                         
48639                     },
48640                     tabIndex:-1
48641                 });
48642             }
48643             
48644             
48645             tb.add(smenu);
48646             
48647             
48648         }
48649         
48650         var cmenu = { };
48651         if (!this.disable.cleanStyles) {
48652             cmenu = {
48653                 cls: 'x-btn-icon x-btn-clear',
48654                 
48655                 menu : {
48656                     items : []
48657                 }
48658             };
48659             for (var i =0; i < this.cleanStyles.length; i++) {
48660                 cmenu.menu.items.push({
48661                     actiontype : this.cleanStyles[i],
48662                     html: 'Remove ' + this.cleanStyles[i],
48663                     handler: function(a,b) {
48664 //                        Roo.log(a);
48665 //                        Roo.log(b);
48666                         var c = Roo.get(editorcore.doc.body);
48667                         c.select('[style]').each(function(s) {
48668                             s.dom.style.removeProperty(a.actiontype);
48669                         });
48670                         editorcore.syncValue();
48671                     },
48672                     tabIndex:-1
48673                 });
48674             }
48675             cmenu.menu.items.push({
48676                 actiontype : 'tablewidths',
48677                 html: 'Remove Table Widths',
48678                 handler: function(a,b) {
48679                     editorcore.cleanTableWidths();
48680                     editorcore.syncValue();
48681                 },
48682                 tabIndex:-1
48683             });
48684             cmenu.menu.items.push({
48685                 actiontype : 'word',
48686                 html: 'Remove MS Word Formating',
48687                 handler: function(a,b) {
48688                     editorcore.cleanWord();
48689                     editorcore.syncValue();
48690                 },
48691                 tabIndex:-1
48692             });
48693             
48694             cmenu.menu.items.push({
48695                 actiontype : 'all',
48696                 html: 'Remove All Styles',
48697                 handler: function(a,b) {
48698                     
48699                     var c = Roo.get(editorcore.doc.body);
48700                     c.select('[style]').each(function(s) {
48701                         s.dom.removeAttribute('style');
48702                     });
48703                     editorcore.syncValue();
48704                 },
48705                 tabIndex:-1
48706             });
48707             
48708             cmenu.menu.items.push({
48709                 actiontype : 'all',
48710                 html: 'Remove All CSS Classes',
48711                 handler: function(a,b) {
48712                     
48713                     var c = Roo.get(editorcore.doc.body);
48714                     c.select('[class]').each(function(s) {
48715                         s.dom.removeAttribute('class');
48716                     });
48717                     editorcore.cleanWord();
48718                     editorcore.syncValue();
48719                 },
48720                 tabIndex:-1
48721             });
48722             
48723              cmenu.menu.items.push({
48724                 actiontype : 'tidy',
48725                 html: 'Tidy HTML Source',
48726                 handler: function(a,b) {
48727                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48728                     editorcore.syncValue();
48729                 },
48730                 tabIndex:-1
48731             });
48732             
48733             
48734             tb.add(cmenu);
48735         }
48736          
48737         if (!this.disable.specialElements) {
48738             var semenu = {
48739                 text: "Other;",
48740                 cls: 'x-edit-none',
48741                 menu : {
48742                     items : []
48743                 }
48744             };
48745             for (var i =0; i < this.specialElements.length; i++) {
48746                 semenu.menu.items.push(
48747                     Roo.apply({ 
48748                         handler: function(a,b) {
48749                             editor.insertAtCursor(this.ihtml);
48750                         }
48751                     }, this.specialElements[i])
48752                 );
48753                     
48754             }
48755             
48756             tb.add(semenu);
48757             
48758             
48759         }
48760          
48761         
48762         if (this.btns) {
48763             for(var i =0; i< this.btns.length;i++) {
48764                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
48765                 b.cls =  'x-edit-none';
48766                 
48767                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48768                     b.cls += ' x-init-enable';
48769                 }
48770                 
48771                 b.scope = editorcore;
48772                 tb.add(b);
48773             }
48774         
48775         }
48776         
48777         
48778         
48779         // disable everything...
48780         
48781         this.tb.items.each(function(item){
48782             
48783            if(
48784                 item.id != editorcore.frameId+ '-sourceedit' && 
48785                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48786             ){
48787                 
48788                 item.disable();
48789             }
48790         });
48791         this.rendered = true;
48792         
48793         // the all the btns;
48794         editor.on('editorevent', this.updateToolbar, this);
48795         // other toolbars need to implement this..
48796         //editor.on('editmodechange', this.updateToolbar, this);
48797     },
48798     
48799     
48800     relayBtnCmd : function(btn) {
48801         this.editorcore.relayCmd(btn.cmd);
48802     },
48803     // private used internally
48804     createLink : function(){
48805         Roo.log("create link?");
48806         var url = prompt(this.createLinkText, this.defaultLinkValue);
48807         if(url && url != 'http:/'+'/'){
48808             this.editorcore.relayCmd('createlink', url);
48809         }
48810     },
48811
48812     
48813     /**
48814      * Protected method that will not generally be called directly. It triggers
48815      * a toolbar update by reading the markup state of the current selection in the editor.
48816      */
48817     updateToolbar: function(){
48818
48819         if(!this.editorcore.activated){
48820             this.editor.onFirstFocus();
48821             return;
48822         }
48823
48824         var btns = this.tb.items.map, 
48825             doc = this.editorcore.doc,
48826             frameId = this.editorcore.frameId;
48827
48828         if(!this.disable.font && !Roo.isSafari){
48829             /*
48830             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48831             if(name != this.fontSelect.dom.value){
48832                 this.fontSelect.dom.value = name;
48833             }
48834             */
48835         }
48836         if(!this.disable.format){
48837             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48838             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48839             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48840             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48841         }
48842         if(!this.disable.alignments){
48843             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48844             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48845             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48846         }
48847         if(!Roo.isSafari && !this.disable.lists){
48848             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48849             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48850         }
48851         
48852         var ans = this.editorcore.getAllAncestors();
48853         if (this.formatCombo) {
48854             
48855             
48856             var store = this.formatCombo.store;
48857             this.formatCombo.setValue("");
48858             for (var i =0; i < ans.length;i++) {
48859                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48860                     // select it..
48861                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48862                     break;
48863                 }
48864             }
48865         }
48866         
48867         
48868         
48869         // hides menus... - so this cant be on a menu...
48870         Roo.menu.MenuMgr.hideAll();
48871
48872         //this.editorsyncValue();
48873     },
48874    
48875     
48876     createFontOptions : function(){
48877         var buf = [], fs = this.fontFamilies, ff, lc;
48878         
48879         
48880         
48881         for(var i = 0, len = fs.length; i< len; i++){
48882             ff = fs[i];
48883             lc = ff.toLowerCase();
48884             buf.push(
48885                 '<option value="',lc,'" style="font-family:',ff,';"',
48886                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48887                     ff,
48888                 '</option>'
48889             );
48890         }
48891         return buf.join('');
48892     },
48893     
48894     toggleSourceEdit : function(sourceEditMode){
48895         
48896         Roo.log("toolbar toogle");
48897         if(sourceEditMode === undefined){
48898             sourceEditMode = !this.sourceEditMode;
48899         }
48900         this.sourceEditMode = sourceEditMode === true;
48901         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48902         // just toggle the button?
48903         if(btn.pressed !== this.sourceEditMode){
48904             btn.toggle(this.sourceEditMode);
48905             return;
48906         }
48907         
48908         if(sourceEditMode){
48909             Roo.log("disabling buttons");
48910             this.tb.items.each(function(item){
48911                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48912                     item.disable();
48913                 }
48914             });
48915           
48916         }else{
48917             Roo.log("enabling buttons");
48918             if(this.editorcore.initialized){
48919                 this.tb.items.each(function(item){
48920                     item.enable();
48921                 });
48922             }
48923             
48924         }
48925         Roo.log("calling toggole on editor");
48926         // tell the editor that it's been pressed..
48927         this.editor.toggleSourceEdit(sourceEditMode);
48928        
48929     },
48930      /**
48931      * Object collection of toolbar tooltips for the buttons in the editor. The key
48932      * is the command id associated with that button and the value is a valid QuickTips object.
48933      * For example:
48934 <pre><code>
48935 {
48936     bold : {
48937         title: 'Bold (Ctrl+B)',
48938         text: 'Make the selected text bold.',
48939         cls: 'x-html-editor-tip'
48940     },
48941     italic : {
48942         title: 'Italic (Ctrl+I)',
48943         text: 'Make the selected text italic.',
48944         cls: 'x-html-editor-tip'
48945     },
48946     ...
48947 </code></pre>
48948     * @type Object
48949      */
48950     buttonTips : {
48951         bold : {
48952             title: 'Bold (Ctrl+B)',
48953             text: 'Make the selected text bold.',
48954             cls: 'x-html-editor-tip'
48955         },
48956         italic : {
48957             title: 'Italic (Ctrl+I)',
48958             text: 'Make the selected text italic.',
48959             cls: 'x-html-editor-tip'
48960         },
48961         underline : {
48962             title: 'Underline (Ctrl+U)',
48963             text: 'Underline the selected text.',
48964             cls: 'x-html-editor-tip'
48965         },
48966         strikethrough : {
48967             title: 'Strikethrough',
48968             text: 'Strikethrough the selected text.',
48969             cls: 'x-html-editor-tip'
48970         },
48971         increasefontsize : {
48972             title: 'Grow Text',
48973             text: 'Increase the font size.',
48974             cls: 'x-html-editor-tip'
48975         },
48976         decreasefontsize : {
48977             title: 'Shrink Text',
48978             text: 'Decrease the font size.',
48979             cls: 'x-html-editor-tip'
48980         },
48981         backcolor : {
48982             title: 'Text Highlight Color',
48983             text: 'Change the background color of the selected text.',
48984             cls: 'x-html-editor-tip'
48985         },
48986         forecolor : {
48987             title: 'Font Color',
48988             text: 'Change the color of the selected text.',
48989             cls: 'x-html-editor-tip'
48990         },
48991         justifyleft : {
48992             title: 'Align Text Left',
48993             text: 'Align text to the left.',
48994             cls: 'x-html-editor-tip'
48995         },
48996         justifycenter : {
48997             title: 'Center Text',
48998             text: 'Center text in the editor.',
48999             cls: 'x-html-editor-tip'
49000         },
49001         justifyright : {
49002             title: 'Align Text Right',
49003             text: 'Align text to the right.',
49004             cls: 'x-html-editor-tip'
49005         },
49006         insertunorderedlist : {
49007             title: 'Bullet List',
49008             text: 'Start a bulleted list.',
49009             cls: 'x-html-editor-tip'
49010         },
49011         insertorderedlist : {
49012             title: 'Numbered List',
49013             text: 'Start a numbered list.',
49014             cls: 'x-html-editor-tip'
49015         },
49016         createlink : {
49017             title: 'Hyperlink',
49018             text: 'Make the selected text a hyperlink.',
49019             cls: 'x-html-editor-tip'
49020         },
49021         sourceedit : {
49022             title: 'Source Edit',
49023             text: 'Switch to source editing mode.',
49024             cls: 'x-html-editor-tip'
49025         }
49026     },
49027     // private
49028     onDestroy : function(){
49029         if(this.rendered){
49030             
49031             this.tb.items.each(function(item){
49032                 if(item.menu){
49033                     item.menu.removeAll();
49034                     if(item.menu.el){
49035                         item.menu.el.destroy();
49036                     }
49037                 }
49038                 item.destroy();
49039             });
49040              
49041         }
49042     },
49043     onFirstFocus: function() {
49044         this.tb.items.each(function(item){
49045            item.enable();
49046         });
49047     }
49048 });
49049
49050
49051
49052
49053 // <script type="text/javascript">
49054 /*
49055  * Based on
49056  * Ext JS Library 1.1.1
49057  * Copyright(c) 2006-2007, Ext JS, LLC.
49058  *  
49059  
49060  */
49061
49062  
49063 /**
49064  * @class Roo.form.HtmlEditor.ToolbarContext
49065  * Context Toolbar
49066  * 
49067  * Usage:
49068  *
49069  new Roo.form.HtmlEditor({
49070     ....
49071     toolbars : [
49072         { xtype: 'ToolbarStandard', styles : {} }
49073         { xtype: 'ToolbarContext', disable : {} }
49074     ]
49075 })
49076
49077      
49078  * 
49079  * @config : {Object} disable List of elements to disable.. (not done yet.)
49080  * @config : {Object} styles  Map of styles available.
49081  * 
49082  */
49083
49084 Roo.form.HtmlEditor.ToolbarContext = function(config)
49085 {
49086     
49087     Roo.apply(this, config);
49088     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49089     // dont call parent... till later.
49090     this.styles = this.styles || {};
49091 }
49092
49093  
49094
49095 Roo.form.HtmlEditor.ToolbarContext.types = {
49096     'IMG' : {
49097         width : {
49098             title: "Width",
49099             width: 40
49100         },
49101         height:  {
49102             title: "Height",
49103             width: 40
49104         },
49105         align: {
49106             title: "Align",
49107             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49108             width : 80
49109             
49110         },
49111         border: {
49112             title: "Border",
49113             width: 40
49114         },
49115         alt: {
49116             title: "Alt",
49117             width: 120
49118         },
49119         src : {
49120             title: "Src",
49121             width: 220
49122         }
49123         
49124     },
49125     
49126     'FIGURE' : {
49127          align: {
49128             title: "Align",
49129             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49130             width : 80  
49131         }
49132     },
49133     'A' : {
49134         name : {
49135             title: "Name",
49136             width: 50
49137         },
49138         target:  {
49139             title: "Target",
49140             width: 120
49141         },
49142         href:  {
49143             title: "Href",
49144             width: 220
49145         } // border?
49146         
49147     },
49148     /*
49149     'TABLE' : {
49150         rows : {
49151             title: "Rows",
49152             width: 20
49153         },
49154         cols : {
49155             title: "Cols",
49156             width: 20
49157         },
49158         width : {
49159             title: "Width",
49160             width: 40
49161         },
49162         height : {
49163             title: "Height",
49164             width: 40
49165         },
49166         border : {
49167             title: "Border",
49168             width: 20
49169         }
49170     },
49171     'TD' : {
49172         width : {
49173             title: "Width",
49174             width: 40
49175         },
49176         height : {
49177             title: "Height",
49178             width: 40
49179         },   
49180         align: {
49181             title: "Align",
49182             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
49183             width: 80
49184         },
49185         valign: {
49186             title: "Valign",
49187             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
49188             width: 80
49189         },
49190         colspan: {
49191             title: "Colspan",
49192             width: 20
49193             
49194         },
49195          'font-family'  : {
49196             title : "Font",
49197             style : 'fontFamily',
49198             displayField: 'display',
49199             optname : 'font-family',
49200             width: 140
49201         }
49202     },
49203     */
49204     'INPUT' : {
49205         name : {
49206             title: "name",
49207             width: 120
49208         },
49209         value : {
49210             title: "Value",
49211             width: 120
49212         },
49213         width : {
49214             title: "Width",
49215             width: 40
49216         }
49217     },
49218     'LABEL' : {
49219         'for' : {
49220             title: "For",
49221             width: 120
49222         }
49223     },
49224     'TEXTAREA' : {
49225         name : {
49226             title: "name",
49227             width: 120
49228         },
49229         rows : {
49230             title: "Rows",
49231             width: 20
49232         },
49233         cols : {
49234             title: "Cols",
49235             width: 20
49236         }
49237     },
49238     'SELECT' : {
49239         name : {
49240             title: "name",
49241             width: 120
49242         },
49243         selectoptions : {
49244             title: "Options",
49245             width: 200
49246         }
49247     },
49248     
49249     // should we really allow this??
49250     // should this just be 
49251     'BODY' : {
49252         title : {
49253             title: "Title",
49254             width: 200,
49255             disabled : true
49256         }
49257     },
49258     /*
49259     'SPAN' : {
49260         'font-family'  : {
49261             title : "Font",
49262             style : 'fontFamily',
49263             displayField: 'display',
49264             optname : 'font-family',
49265             width: 140
49266         }
49267     },
49268     'DIV' : {
49269         'font-family'  : {
49270             title : "Font",
49271             style : 'fontFamily',
49272             displayField: 'display',
49273             optname : 'font-family',
49274             width: 140
49275         }
49276     },
49277      'P' : {
49278         'font-family'  : {
49279             title : "Font",
49280             style : 'fontFamily',
49281             displayField: 'display',
49282             optname : 'font-family',
49283             width: 140
49284         }
49285     },
49286     */
49287     '*' : {
49288         // empty..
49289     }
49290
49291 };
49292
49293 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49294 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49295
49296 Roo.form.HtmlEditor.ToolbarContext.options = {
49297         'font-family'  : [ 
49298                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49299                 [ 'Courier New', 'Courier New'],
49300                 [ 'Tahoma', 'Tahoma'],
49301                 [ 'Times New Roman,serif', 'Times'],
49302                 [ 'Verdana','Verdana' ]
49303         ]
49304 };
49305
49306 // fixme - these need to be configurable..
49307  
49308
49309 //Roo.form.HtmlEditor.ToolbarContext.types
49310
49311
49312 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49313     
49314     tb: false,
49315     
49316     rendered: false,
49317     
49318     editor : false,
49319     editorcore : false,
49320     /**
49321      * @cfg {Object} disable  List of toolbar elements to disable
49322          
49323      */
49324     disable : false,
49325     /**
49326      * @cfg {Object} styles List of styles 
49327      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49328      *
49329      * These must be defined in the page, so they get rendered correctly..
49330      * .headline { }
49331      * TD.underline { }
49332      * 
49333      */
49334     styles : false,
49335     
49336     options: false,
49337     
49338     toolbars : false,
49339     
49340     init : function(editor)
49341     {
49342         this.editor = editor;
49343         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49344         var editorcore = this.editorcore;
49345         
49346         var fid = editorcore.frameId;
49347         var etb = this;
49348         function btn(id, toggle, handler){
49349             var xid = fid + '-'+ id ;
49350             return {
49351                 id : xid,
49352                 cmd : id,
49353                 cls : 'x-btn-icon x-edit-'+id,
49354                 enableToggle:toggle !== false,
49355                 scope: editorcore, // was editor...
49356                 handler:handler||editorcore.relayBtnCmd,
49357                 clickEvent:'mousedown',
49358                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49359                 tabIndex:-1
49360             };
49361         }
49362         // create a new element.
49363         var wdiv = editor.wrap.createChild({
49364                 tag: 'div'
49365             }, editor.wrap.dom.firstChild.nextSibling, true);
49366         
49367         // can we do this more than once??
49368         
49369          // stop form submits
49370       
49371  
49372         // disable everything...
49373         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49374         this.toolbars = {};
49375            
49376         for (var i in  ty) {
49377           
49378             this.toolbars[i] = this.buildToolbar(ty[i],i);
49379         }
49380         this.tb = this.toolbars.BODY;
49381         this.tb.el.show();
49382         this.buildFooter();
49383         this.footer.show();
49384         editor.on('hide', function( ) { this.footer.hide() }, this);
49385         editor.on('show', function( ) { this.footer.show() }, this);
49386         
49387          
49388         this.rendered = true;
49389         
49390         // the all the btns;
49391         editor.on('editorevent', this.updateToolbar, this);
49392         // other toolbars need to implement this..
49393         //editor.on('editmodechange', this.updateToolbar, this);
49394     },
49395     
49396     
49397     
49398     /**
49399      * Protected method that will not generally be called directly. It triggers
49400      * a toolbar update by reading the markup state of the current selection in the editor.
49401      *
49402      * Note you can force an update by calling on('editorevent', scope, false)
49403      */
49404     updateToolbar: function(editor ,ev, sel)
49405     {
49406         
49407         if (ev) {
49408             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49409         }
49410         
49411         //Roo.log(ev);
49412         // capture mouse up - this is handy for selecting images..
49413         // perhaps should go somewhere else...
49414         if(!this.editorcore.activated){
49415              this.editor.onFirstFocus();
49416             return;
49417         }
49418         Roo.log(ev ? ev.target : 'NOTARGET');
49419         
49420         
49421         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49422         // selectNode - might want to handle IE?
49423         
49424         
49425         
49426         if (ev &&
49427             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49428             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49429             // they have click on an image...
49430             // let's see if we can change the selection...
49431             sel = ev.target;
49432             
49433             // this triggers looping?
49434             //this.editorcore.selectNode(sel);
49435              
49436         }  
49437         
49438       
49439         //var updateFooter = sel ? false : true; 
49440         
49441         
49442         var ans = this.editorcore.getAllAncestors();
49443         
49444         // pick
49445         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49446         
49447         if (!sel) { 
49448             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49449             sel = sel ? sel : this.editorcore.doc.body;
49450             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49451             
49452         }
49453         
49454         var tn = sel.tagName.toUpperCase();
49455         var lastSel = this.tb.selectedNode;
49456         this.tb.selectedNode = sel;
49457         var left_label = tn;
49458         
49459         // ok see if we are editing a block?
49460         var sel_el = Roo.get(sel);
49461         var db = false;
49462         // you are not actually selecting the block.
49463         if (sel && sel.hasAttribute('data-block')) {
49464             db = sel;
49465         } else if (sel && !sel.hasAttribute('contenteditable')) {
49466             db = sel_el.findParent('[data-block]');
49467             var cepar = sel_el.findParent('[contenteditable=true]');
49468             if (db && cepar && cepar.tagName != 'BODY') {
49469                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49470             }   
49471         }
49472         
49473         
49474         var block = false;
49475         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49476         if (db) {
49477             block = Roo.htmleditor.Block.factory(db);
49478             if (block) {
49479                 tn = 'BLOCK.' + db.getAttribute('data-block');
49480                 
49481                 //this.editorcore.selectNode(db);
49482                 if (typeof(this.toolbars[tn]) == 'undefined') {
49483                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49484                 }
49485                 this.toolbars[tn].selectedNode = db;
49486                 left_label = block.friendly_name;
49487                 ans = this.editorcore.getAllAncestors();
49488             }
49489             
49490                 
49491             
49492         }
49493         
49494         
49495         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49496             return; // no change?
49497         }
49498         
49499         
49500           
49501         this.tb.el.hide();
49502         ///console.log("show: " + tn);
49503         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49504         
49505         this.tb.el.show();
49506         // update name
49507         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49508         
49509         
49510         // update attributes
49511         if (block) {
49512              
49513             this.tb.fields.each(function(e) {
49514                 e.setValue(block[e.name]);
49515             });
49516             
49517             
49518         } else  if (this.tb.fields && this.tb.selectedNode) {
49519             this.tb.fields.each( function(e) {
49520                 if (e.stylename) {
49521                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49522                     return;
49523                 } 
49524                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49525             }, this);
49526             this.updateToolbarStyles(this.tb.selectedNode);  
49527         }
49528         
49529         
49530        
49531         Roo.menu.MenuMgr.hideAll();
49532
49533         
49534         
49535     
49536         // update the footer
49537         //
49538         this.updateFooter(ans);
49539              
49540     },
49541     
49542     updateToolbarStyles : function(sel)
49543     {
49544         var hasStyles = false;
49545         for(var i in this.styles) {
49546             hasStyles = true;
49547             break;
49548         }
49549         
49550         // update styles
49551         if (hasStyles && this.tb.hasStyles) { 
49552             var st = this.tb.fields.item(0);
49553             
49554             st.store.removeAll();
49555             var cn = sel.className.split(/\s+/);
49556             
49557             var avs = [];
49558             if (this.styles['*']) {
49559                 
49560                 Roo.each(this.styles['*'], function(v) {
49561                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49562                 });
49563             }
49564             if (this.styles[tn]) { 
49565                 Roo.each(this.styles[tn], function(v) {
49566                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49567                 });
49568             }
49569             
49570             st.store.loadData(avs);
49571             st.collapse();
49572             st.setValue(cn);
49573         }
49574     },
49575     
49576      
49577     updateFooter : function(ans)
49578     {
49579         var html = '';
49580         if (ans === false) {
49581             this.footDisp.dom.innerHTML = '';
49582             return;
49583         }
49584         
49585         this.footerEls = ans.reverse();
49586         Roo.each(this.footerEls, function(a,i) {
49587             if (!a) { return; }
49588             html += html.length ? ' &gt; '  :  '';
49589             
49590             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49591             
49592         });
49593        
49594         // 
49595         var sz = this.footDisp.up('td').getSize();
49596         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49597         this.footDisp.dom.style.marginLeft = '5px';
49598         
49599         this.footDisp.dom.style.overflow = 'hidden';
49600         
49601         this.footDisp.dom.innerHTML = html;
49602             
49603         
49604     },
49605    
49606        
49607     // private
49608     onDestroy : function(){
49609         if(this.rendered){
49610             
49611             this.tb.items.each(function(item){
49612                 if(item.menu){
49613                     item.menu.removeAll();
49614                     if(item.menu.el){
49615                         item.menu.el.destroy();
49616                     }
49617                 }
49618                 item.destroy();
49619             });
49620              
49621         }
49622     },
49623     onFirstFocus: function() {
49624         // need to do this for all the toolbars..
49625         this.tb.items.each(function(item){
49626            item.enable();
49627         });
49628     },
49629     buildToolbar: function(tlist, nm, friendly_name, block)
49630     {
49631         var editor = this.editor;
49632         var editorcore = this.editorcore;
49633          // create a new element.
49634         var wdiv = editor.wrap.createChild({
49635                 tag: 'div'
49636             }, editor.wrap.dom.firstChild.nextSibling, true);
49637         
49638        
49639         var tb = new Roo.Toolbar(wdiv);
49640         this.tb = tb;
49641         if (tlist === false && block) {
49642             tlist = block.contextMenu(this);
49643         }
49644         
49645         tb.hasStyles = false;
49646         tb.name = nm;
49647         
49648         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49649         
49650         var styles = Array.from(this.styles);
49651         
49652         
49653         // styles...
49654         if (styles && styles.length) {
49655             tb.hasStyles = true;
49656             // this needs a multi-select checkbox...
49657             tb.addField( new Roo.form.ComboBox({
49658                 store: new Roo.data.SimpleStore({
49659                     id : 'val',
49660                     fields: ['val', 'selected'],
49661                     data : [] 
49662                 }),
49663                 name : '-roo-edit-className',
49664                 attrname : 'className',
49665                 displayField: 'val',
49666                 typeAhead: false,
49667                 mode: 'local',
49668                 editable : false,
49669                 triggerAction: 'all',
49670                 emptyText:'Select Style',
49671                 selectOnFocus:true,
49672                 width: 130,
49673                 listeners : {
49674                     'select': function(c, r, i) {
49675                         // initial support only for on class per el..
49676                         tb.selectedNode.className =  r ? r.get('val') : '';
49677                         editorcore.syncValue();
49678                     }
49679                 }
49680     
49681             }));
49682         }
49683         
49684         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49685         
49686         
49687         for (var i in tlist) {
49688             
49689             // newer versions will use xtype cfg to create menus.
49690             if (typeof(tlist[i].xtype) != 'undefined') {
49691                 
49692                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49693                 
49694                 
49695                 continue;
49696             }
49697             
49698             var item = tlist[i];
49699             tb.add(item.title + ":&nbsp;");
49700             
49701             
49702             //optname == used so you can configure the options available..
49703             var opts = item.opts ? item.opts : false;
49704             if (item.optname) { // use the b
49705                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49706            
49707             }
49708             
49709             if (opts) {
49710                 // opts == pulldown..
49711                 tb.addField( new Roo.form.ComboBox({
49712                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49713                         id : 'val',
49714                         fields: ['val', 'display'],
49715                         data : opts  
49716                     }),
49717                     name : '-roo-edit-' + i,
49718                     
49719                     attrname : i,
49720                     stylename : item.style ? item.style : false,
49721                     
49722                     displayField: item.displayField ? item.displayField : 'val',
49723                     valueField :  'val',
49724                     typeAhead: false,
49725                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
49726                     editable : false,
49727                     triggerAction: 'all',
49728                     emptyText:'Select',
49729                     selectOnFocus:true,
49730                     width: item.width ? item.width  : 130,
49731                     listeners : {
49732                         'select': function(c, r, i) {
49733                             if (tb.selectedNode.hasAttribute('data-block')) {
49734                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49735                                 b[c.attrname] = r.get('val');
49736                                 b.updateElement(tb.selectedNode);
49737                                 editorcore.syncValue();
49738                                 return;
49739                             }
49740                             
49741                             if (c.stylename) {
49742                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49743                                 editorcore.syncValue();
49744                                 return;
49745                             }
49746                             if (r === false) {
49747                                 tb.selectedNode.removeAttribute(c.attrname);
49748                                 editorcore.syncValue();
49749                                 return;
49750                             }
49751                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49752                             editorcore.syncValue();
49753                         }
49754                     }
49755
49756                 }));
49757                 continue;
49758                     
49759                  
49760                 /*
49761                 tb.addField( new Roo.form.TextField({
49762                     name: i,
49763                     width: 100,
49764                     //allowBlank:false,
49765                     value: ''
49766                 }));
49767                 continue;
49768                 */
49769             }
49770             tb.addField( new Roo.form.TextField({
49771                 name: '-roo-edit-' + i,
49772                 attrname : i,
49773                 
49774                 width: item.width,
49775                 //allowBlank:true,
49776                 value: '',
49777                 listeners: {
49778                     'change' : function(f, nv, ov) {
49779                         
49780                         if (tb.selectedNode.hasAttribute('data-block')) {
49781                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49782                             b[f.attrname] = nv;
49783                             b.updateElement(tb.selectedNode);
49784                             editorcore.syncValue();
49785                             return;
49786                         }
49787                         
49788                         tb.selectedNode.setAttribute(f.attrname, nv);
49789                         editorcore.syncValue();
49790                     }
49791                 }
49792             }));
49793              
49794         }
49795         
49796         var _this = this;
49797         
49798         if(nm == 'BODY'){
49799             tb.addSeparator();
49800         
49801             tb.addButton( {
49802                 text: 'Stylesheets',
49803
49804                 listeners : {
49805                     click : function ()
49806                     {
49807                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49808                     }
49809                 }
49810             });
49811         }
49812         
49813         tb.addFill();
49814         tb.addButton({
49815             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49816     
49817             listeners : {
49818                 click : function ()
49819                 {
49820                     // remove
49821                     // undo does not work.
49822                     var sn = tb.selectedNode;
49823                     if (!sn) {
49824                         return;
49825                     }
49826                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49827                     if (sn.hasAttribute('data-block')) {
49828                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49829                         sn.parentNode.removeChild(sn);
49830                         
49831                     } else if (sn && sn.tagName != 'BODY') {
49832                         // remove and keep parents.
49833                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49834                         a.removeTag(sn);
49835                     }
49836                     
49837                     
49838                     var range = editorcore.createRange();
49839         
49840                     range.setStart(stn,0);
49841                     range.setEnd(stn,0); 
49842                     var selection = editorcore.getSelection();
49843                     selection.removeAllRanges();
49844                     selection.addRange(range);
49845                     
49846                     
49847                     //_this.updateToolbar(null, null, pn);
49848                     _this.updateToolbar(null, null, null);
49849                     _this.updateFooter(false);
49850                     
49851                 }
49852             }
49853             
49854                     
49855                 
49856             
49857         });
49858         
49859         
49860         tb.el.on('click', function(e){
49861             e.preventDefault(); // what does this do?
49862         });
49863         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49864         tb.el.hide();
49865         
49866         // dont need to disable them... as they will get hidden
49867         return tb;
49868          
49869         
49870     },
49871     buildFooter : function()
49872     {
49873         
49874         var fel = this.editor.wrap.createChild();
49875         this.footer = new Roo.Toolbar(fel);
49876         // toolbar has scrolly on left / right?
49877         var footDisp= new Roo.Toolbar.Fill();
49878         var _t = this;
49879         this.footer.add(
49880             {
49881                 text : '&lt;',
49882                 xtype: 'Button',
49883                 handler : function() {
49884                     _t.footDisp.scrollTo('left',0,true)
49885                 }
49886             }
49887         );
49888         this.footer.add( footDisp );
49889         this.footer.add( 
49890             {
49891                 text : '&gt;',
49892                 xtype: 'Button',
49893                 handler : function() {
49894                     // no animation..
49895                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49896                 }
49897             }
49898         );
49899         var fel = Roo.get(footDisp.el);
49900         fel.addClass('x-editor-context');
49901         this.footDispWrap = fel; 
49902         this.footDispWrap.overflow  = 'hidden';
49903         
49904         this.footDisp = fel.createChild();
49905         this.footDispWrap.on('click', this.onContextClick, this)
49906         
49907         
49908     },
49909     // when the footer contect changes
49910     onContextClick : function (ev,dom)
49911     {
49912         ev.preventDefault();
49913         var  cn = dom.className;
49914         //Roo.log(cn);
49915         if (!cn.match(/x-ed-loc-/)) {
49916             return;
49917         }
49918         var n = cn.split('-').pop();
49919         var ans = this.footerEls;
49920         var sel = ans[n];
49921         
49922          // pick
49923         var range = this.editorcore.createRange();
49924         
49925         range.selectNodeContents(sel);
49926         //range.selectNode(sel);
49927         
49928         
49929         var selection = this.editorcore.getSelection();
49930         selection.removeAllRanges();
49931         selection.addRange(range);
49932         
49933         
49934         
49935         this.updateToolbar(null, null, sel);
49936         
49937         
49938     }
49939     
49940     
49941     
49942     
49943     
49944 });
49945
49946
49947
49948
49949
49950 /*
49951  * Based on:
49952  * Ext JS Library 1.1.1
49953  * Copyright(c) 2006-2007, Ext JS, LLC.
49954  *
49955  * Originally Released Under LGPL - original licence link has changed is not relivant.
49956  *
49957  * Fork - LGPL
49958  * <script type="text/javascript">
49959  */
49960  
49961 /**
49962  * @class Roo.form.BasicForm
49963  * @extends Roo.util.Observable
49964  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49965  * @constructor
49966  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49967  * @param {Object} config Configuration options
49968  */
49969 Roo.form.BasicForm = function(el, config){
49970     this.allItems = [];
49971     this.childForms = [];
49972     Roo.apply(this, config);
49973     /*
49974      * The Roo.form.Field items in this form.
49975      * @type MixedCollection
49976      */
49977      
49978      
49979     this.items = new Roo.util.MixedCollection(false, function(o){
49980         return o.id || (o.id = Roo.id());
49981     });
49982     this.addEvents({
49983         /**
49984          * @event beforeaction
49985          * Fires before any action is performed. Return false to cancel the action.
49986          * @param {Form} this
49987          * @param {Action} action The action to be performed
49988          */
49989         beforeaction: true,
49990         /**
49991          * @event actionfailed
49992          * Fires when an action fails.
49993          * @param {Form} this
49994          * @param {Action} action The action that failed
49995          */
49996         actionfailed : true,
49997         /**
49998          * @event actioncomplete
49999          * Fires when an action is completed.
50000          * @param {Form} this
50001          * @param {Action} action The action that completed
50002          */
50003         actioncomplete : true
50004     });
50005     if(el){
50006         this.initEl(el);
50007     }
50008     Roo.form.BasicForm.superclass.constructor.call(this);
50009     
50010     Roo.form.BasicForm.popover.apply();
50011 };
50012
50013 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50014     /**
50015      * @cfg {String} method
50016      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50017      */
50018     /**
50019      * @cfg {DataReader} reader
50020      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50021      * This is optional as there is built-in support for processing JSON.
50022      */
50023     /**
50024      * @cfg {DataReader} errorReader
50025      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50026      * This is completely optional as there is built-in support for processing JSON.
50027      */
50028     /**
50029      * @cfg {String} url
50030      * The URL to use for form actions if one isn't supplied in the action options.
50031      */
50032     /**
50033      * @cfg {Boolean} fileUpload
50034      * Set to true if this form is a file upload.
50035      */
50036      
50037     /**
50038      * @cfg {Object} baseParams
50039      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50040      */
50041      /**
50042      
50043     /**
50044      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50045      */
50046     timeout: 30,
50047
50048     // private
50049     activeAction : null,
50050
50051     /**
50052      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50053      * or setValues() data instead of when the form was first created.
50054      */
50055     trackResetOnLoad : false,
50056     
50057     
50058     /**
50059      * childForms - used for multi-tab forms
50060      * @type {Array}
50061      */
50062     childForms : false,
50063     
50064     /**
50065      * allItems - full list of fields.
50066      * @type {Array}
50067      */
50068     allItems : false,
50069     
50070     /**
50071      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50072      * element by passing it or its id or mask the form itself by passing in true.
50073      * @type Mixed
50074      */
50075     waitMsgTarget : false,
50076     
50077     /**
50078      * @type Boolean
50079      */
50080     disableMask : false,
50081     
50082     /**
50083      * @cfg {Boolean} errorMask (true|false) default false
50084      */
50085     errorMask : false,
50086     
50087     /**
50088      * @cfg {Number} maskOffset Default 100
50089      */
50090     maskOffset : 100,
50091
50092     // private
50093     initEl : function(el){
50094         this.el = Roo.get(el);
50095         this.id = this.el.id || Roo.id();
50096         this.el.on('submit', this.onSubmit, this);
50097         this.el.addClass('x-form');
50098     },
50099
50100     // private
50101     onSubmit : function(e){
50102         e.stopEvent();
50103     },
50104
50105     /**
50106      * Returns true if client-side validation on the form is successful.
50107      * @return Boolean
50108      */
50109     isValid : function(){
50110         var valid = true;
50111         var target = false;
50112         this.items.each(function(f){
50113             if(f.validate()){
50114                 return;
50115             }
50116             
50117             valid = false;
50118                 
50119             if(!target && f.el.isVisible(true)){
50120                 target = f;
50121             }
50122         });
50123         
50124         if(this.errorMask && !valid){
50125             Roo.form.BasicForm.popover.mask(this, target);
50126         }
50127         
50128         return valid;
50129     },
50130     /**
50131      * Returns array of invalid form fields.
50132      * @return Array
50133      */
50134     
50135     invalidFields : function()
50136     {
50137         var ret = [];
50138         this.items.each(function(f){
50139             if(f.validate()){
50140                 return;
50141             }
50142             ret.push(f);
50143             
50144         });
50145         
50146         return ret;
50147     },
50148     
50149     
50150     /**
50151      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50152      * @return Boolean
50153      */
50154     isDirty : function(){
50155         var dirty = false;
50156         this.items.each(function(f){
50157            if(f.isDirty()){
50158                dirty = true;
50159                return false;
50160            }
50161         });
50162         return dirty;
50163     },
50164     
50165     /**
50166      * Returns true if any fields in this form have changed since their original load. (New version)
50167      * @return Boolean
50168      */
50169     
50170     hasChanged : function()
50171     {
50172         var dirty = false;
50173         this.items.each(function(f){
50174            if(f.hasChanged()){
50175                dirty = true;
50176                return false;
50177            }
50178         });
50179         return dirty;
50180         
50181     },
50182     /**
50183      * Resets all hasChanged to 'false' -
50184      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50185      * So hasChanged storage is only to be used for this purpose
50186      * @return Boolean
50187      */
50188     resetHasChanged : function()
50189     {
50190         this.items.each(function(f){
50191            f.resetHasChanged();
50192         });
50193         
50194     },
50195     
50196     
50197     /**
50198      * Performs a predefined action (submit or load) or custom actions you define on this form.
50199      * @param {String} actionName The name of the action type
50200      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50201      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50202      * accept other config options):
50203      * <pre>
50204 Property          Type             Description
50205 ----------------  ---------------  ----------------------------------------------------------------------------------
50206 url               String           The url for the action (defaults to the form's url)
50207 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50208 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50209 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50210                                    validate the form on the client (defaults to false)
50211      * </pre>
50212      * @return {BasicForm} this
50213      */
50214     doAction : function(action, options){
50215         if(typeof action == 'string'){
50216             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50217         }
50218         if(this.fireEvent('beforeaction', this, action) !== false){
50219             this.beforeAction(action);
50220             action.run.defer(100, action);
50221         }
50222         return this;
50223     },
50224
50225     /**
50226      * Shortcut to do a submit action.
50227      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50228      * @return {BasicForm} this
50229      */
50230     submit : function(options){
50231         this.doAction('submit', options);
50232         return this;
50233     },
50234
50235     /**
50236      * Shortcut to do a load action.
50237      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50238      * @return {BasicForm} this
50239      */
50240     load : function(options){
50241         this.doAction('load', options);
50242         return this;
50243     },
50244
50245     /**
50246      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50247      * @param {Record} record The record to edit
50248      * @return {BasicForm} this
50249      */
50250     updateRecord : function(record){
50251         record.beginEdit();
50252         var fs = record.fields;
50253         fs.each(function(f){
50254             var field = this.findField(f.name);
50255             if(field){
50256                 record.set(f.name, field.getValue());
50257             }
50258         }, this);
50259         record.endEdit();
50260         return this;
50261     },
50262
50263     /**
50264      * Loads an Roo.data.Record into this form.
50265      * @param {Record} record The record to load
50266      * @return {BasicForm} this
50267      */
50268     loadRecord : function(record){
50269         this.setValues(record.data);
50270         return this;
50271     },
50272
50273     // private
50274     beforeAction : function(action){
50275         var o = action.options;
50276         
50277         if(!this.disableMask) {
50278             if(this.waitMsgTarget === true){
50279                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50280             }else if(this.waitMsgTarget){
50281                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50282                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50283             }else {
50284                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50285             }
50286         }
50287         
50288          
50289     },
50290
50291     // private
50292     afterAction : function(action, success){
50293         this.activeAction = null;
50294         var o = action.options;
50295         
50296         if(!this.disableMask) {
50297             if(this.waitMsgTarget === true){
50298                 this.el.unmask();
50299             }else if(this.waitMsgTarget){
50300                 this.waitMsgTarget.unmask();
50301             }else{
50302                 Roo.MessageBox.updateProgress(1);
50303                 Roo.MessageBox.hide();
50304             }
50305         }
50306         
50307         if(success){
50308             if(o.reset){
50309                 this.reset();
50310             }
50311             Roo.callback(o.success, o.scope, [this, action]);
50312             this.fireEvent('actioncomplete', this, action);
50313             
50314         }else{
50315             
50316             // failure condition..
50317             // we have a scenario where updates need confirming.
50318             // eg. if a locking scenario exists..
50319             // we look for { errors : { needs_confirm : true }} in the response.
50320             if (
50321                 (typeof(action.result) != 'undefined')  &&
50322                 (typeof(action.result.errors) != 'undefined')  &&
50323                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50324            ){
50325                 var _t = this;
50326                 Roo.MessageBox.confirm(
50327                     "Change requires confirmation",
50328                     action.result.errorMsg,
50329                     function(r) {
50330                         if (r != 'yes') {
50331                             return;
50332                         }
50333                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50334                     }
50335                     
50336                 );
50337                 
50338                 
50339                 
50340                 return;
50341             }
50342             
50343             Roo.callback(o.failure, o.scope, [this, action]);
50344             // show an error message if no failed handler is set..
50345             if (!this.hasListener('actionfailed')) {
50346                 Roo.MessageBox.alert("Error",
50347                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50348                         action.result.errorMsg :
50349                         "Saving Failed, please check your entries or try again"
50350                 );
50351             }
50352             
50353             this.fireEvent('actionfailed', this, action);
50354         }
50355         
50356     },
50357
50358     /**
50359      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50360      * @param {String} id The value to search for
50361      * @return Field
50362      */
50363     findField : function(id){
50364         var field = this.items.get(id);
50365         if(!field){
50366             this.items.each(function(f){
50367                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50368                     field = f;
50369                     return false;
50370                 }
50371             });
50372         }
50373         return field || null;
50374     },
50375
50376     /**
50377      * Add a secondary form to this one, 
50378      * Used to provide tabbed forms. One form is primary, with hidden values 
50379      * which mirror the elements from the other forms.
50380      * 
50381      * @param {Roo.form.Form} form to add.
50382      * 
50383      */
50384     addForm : function(form)
50385     {
50386        
50387         if (this.childForms.indexOf(form) > -1) {
50388             // already added..
50389             return;
50390         }
50391         this.childForms.push(form);
50392         var n = '';
50393         Roo.each(form.allItems, function (fe) {
50394             
50395             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50396             if (this.findField(n)) { // already added..
50397                 return;
50398             }
50399             var add = new Roo.form.Hidden({
50400                 name : n
50401             });
50402             add.render(this.el);
50403             
50404             this.add( add );
50405         }, this);
50406         
50407     },
50408     /**
50409      * Mark fields in this form invalid in bulk.
50410      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50411      * @return {BasicForm} this
50412      */
50413     markInvalid : function(errors){
50414         if(errors instanceof Array){
50415             for(var i = 0, len = errors.length; i < len; i++){
50416                 var fieldError = errors[i];
50417                 var f = this.findField(fieldError.id);
50418                 if(f){
50419                     f.markInvalid(fieldError.msg);
50420                 }
50421             }
50422         }else{
50423             var field, id;
50424             for(id in errors){
50425                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50426                     field.markInvalid(errors[id]);
50427                 }
50428             }
50429         }
50430         Roo.each(this.childForms || [], function (f) {
50431             f.markInvalid(errors);
50432         });
50433         
50434         return this;
50435     },
50436
50437     /**
50438      * Set values for fields in this form in bulk.
50439      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50440      * @return {BasicForm} this
50441      */
50442     setValues : function(values){
50443         if(values instanceof Array){ // array of objects
50444             for(var i = 0, len = values.length; i < len; i++){
50445                 var v = values[i];
50446                 var f = this.findField(v.id);
50447                 if(f){
50448                     f.setValue(v.value);
50449                     if(this.trackResetOnLoad){
50450                         f.originalValue = f.getValue();
50451                     }
50452                 }
50453             }
50454         }else{ // object hash
50455             var field, id;
50456             for(id in values){
50457                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50458                     
50459                     if (field.setFromData && 
50460                         field.valueField && 
50461                         field.displayField &&
50462                         // combos' with local stores can 
50463                         // be queried via setValue()
50464                         // to set their value..
50465                         (field.store && !field.store.isLocal)
50466                         ) {
50467                         // it's a combo
50468                         var sd = { };
50469                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50470                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50471                         field.setFromData(sd);
50472                         
50473                     } else {
50474                         field.setValue(values[id]);
50475                     }
50476                     
50477                     
50478                     if(this.trackResetOnLoad){
50479                         field.originalValue = field.getValue();
50480                     }
50481                 }
50482             }
50483         }
50484         this.resetHasChanged();
50485         
50486         
50487         Roo.each(this.childForms || [], function (f) {
50488             f.setValues(values);
50489             f.resetHasChanged();
50490         });
50491                 
50492         return this;
50493     },
50494  
50495     /**
50496      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50497      * they are returned as an array.
50498      * @param {Boolean} asString
50499      * @return {Object}
50500      */
50501     getValues : function(asString){
50502         if (this.childForms) {
50503             // copy values from the child forms
50504             Roo.each(this.childForms, function (f) {
50505                 this.setValues(f.getValues());
50506             }, this);
50507         }
50508         
50509         // use formdata
50510         if (typeof(FormData) != 'undefined' && asString !== true) {
50511             // this relies on a 'recent' version of chrome apparently...
50512             try {
50513                 var fd = (new FormData(this.el.dom)).entries();
50514                 var ret = {};
50515                 var ent = fd.next();
50516                 while (!ent.done) {
50517                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50518                     ent = fd.next();
50519                 };
50520                 return ret;
50521             } catch(e) {
50522                 
50523             }
50524             
50525         }
50526         
50527         
50528         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50529         if(asString === true){
50530             return fs;
50531         }
50532         return Roo.urlDecode(fs);
50533     },
50534     
50535     /**
50536      * Returns the fields in this form as an object with key/value pairs. 
50537      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50538      * @return {Object}
50539      */
50540     getFieldValues : function(with_hidden)
50541     {
50542         if (this.childForms) {
50543             // copy values from the child forms
50544             // should this call getFieldValues - probably not as we do not currently copy
50545             // hidden fields when we generate..
50546             Roo.each(this.childForms, function (f) {
50547                 this.setValues(f.getValues());
50548             }, this);
50549         }
50550         
50551         var ret = {};
50552         this.items.each(function(f){
50553             if (!f.getName()) {
50554                 return;
50555             }
50556             var v = f.getValue();
50557             if (f.inputType =='radio') {
50558                 if (typeof(ret[f.getName()]) == 'undefined') {
50559                     ret[f.getName()] = ''; // empty..
50560                 }
50561                 
50562                 if (!f.el.dom.checked) {
50563                     return;
50564                     
50565                 }
50566                 v = f.el.dom.value;
50567                 
50568             }
50569             
50570             // not sure if this supported any more..
50571             if ((typeof(v) == 'object') && f.getRawValue) {
50572                 v = f.getRawValue() ; // dates..
50573             }
50574             // combo boxes where name != hiddenName...
50575             if (f.name != f.getName()) {
50576                 ret[f.name] = f.getRawValue();
50577             }
50578             ret[f.getName()] = v;
50579         });
50580         
50581         return ret;
50582     },
50583
50584     /**
50585      * Clears all invalid messages in this form.
50586      * @return {BasicForm} this
50587      */
50588     clearInvalid : function(){
50589         this.items.each(function(f){
50590            f.clearInvalid();
50591         });
50592         
50593         Roo.each(this.childForms || [], function (f) {
50594             f.clearInvalid();
50595         });
50596         
50597         
50598         return this;
50599     },
50600
50601     /**
50602      * Resets this form.
50603      * @return {BasicForm} this
50604      */
50605     reset : function(){
50606         this.items.each(function(f){
50607             f.reset();
50608         });
50609         
50610         Roo.each(this.childForms || [], function (f) {
50611             f.reset();
50612         });
50613         this.resetHasChanged();
50614         
50615         return this;
50616     },
50617
50618     /**
50619      * Add Roo.form components to this form.
50620      * @param {Field} field1
50621      * @param {Field} field2 (optional)
50622      * @param {Field} etc (optional)
50623      * @return {BasicForm} this
50624      */
50625     add : function(){
50626         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50627         return this;
50628     },
50629
50630
50631     /**
50632      * Removes a field from the items collection (does NOT remove its markup).
50633      * @param {Field} field
50634      * @return {BasicForm} this
50635      */
50636     remove : function(field){
50637         this.items.remove(field);
50638         return this;
50639     },
50640
50641     /**
50642      * Looks at the fields in this form, checks them for an id attribute,
50643      * and calls applyTo on the existing dom element with that id.
50644      * @return {BasicForm} this
50645      */
50646     render : function(){
50647         this.items.each(function(f){
50648             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50649                 f.applyTo(f.id);
50650             }
50651         });
50652         return this;
50653     },
50654
50655     /**
50656      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50657      * @param {Object} values
50658      * @return {BasicForm} this
50659      */
50660     applyToFields : function(o){
50661         this.items.each(function(f){
50662            Roo.apply(f, o);
50663         });
50664         return this;
50665     },
50666
50667     /**
50668      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50669      * @param {Object} values
50670      * @return {BasicForm} this
50671      */
50672     applyIfToFields : function(o){
50673         this.items.each(function(f){
50674            Roo.applyIf(f, o);
50675         });
50676         return this;
50677     }
50678 });
50679
50680 // back compat
50681 Roo.BasicForm = Roo.form.BasicForm;
50682
50683 Roo.apply(Roo.form.BasicForm, {
50684     
50685     popover : {
50686         
50687         padding : 5,
50688         
50689         isApplied : false,
50690         
50691         isMasked : false,
50692         
50693         form : false,
50694         
50695         target : false,
50696         
50697         intervalID : false,
50698         
50699         maskEl : false,
50700         
50701         apply : function()
50702         {
50703             if(this.isApplied){
50704                 return;
50705             }
50706             
50707             this.maskEl = {
50708                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50709                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50710                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50711                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50712             };
50713             
50714             this.maskEl.top.enableDisplayMode("block");
50715             this.maskEl.left.enableDisplayMode("block");
50716             this.maskEl.bottom.enableDisplayMode("block");
50717             this.maskEl.right.enableDisplayMode("block");
50718             
50719             Roo.get(document.body).on('click', function(){
50720                 this.unmask();
50721             }, this);
50722             
50723             Roo.get(document.body).on('touchstart', function(){
50724                 this.unmask();
50725             }, this);
50726             
50727             this.isApplied = true
50728         },
50729         
50730         mask : function(form, target)
50731         {
50732             this.form = form;
50733             
50734             this.target = target;
50735             
50736             if(!this.form.errorMask || !target.el){
50737                 return;
50738             }
50739             
50740             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50741             
50742             var ot = this.target.el.calcOffsetsTo(scrollable);
50743             
50744             var scrollTo = ot[1] - this.form.maskOffset;
50745             
50746             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50747             
50748             scrollable.scrollTo('top', scrollTo);
50749             
50750             var el = this.target.wrap || this.target.el;
50751             
50752             var box = el.getBox();
50753             
50754             this.maskEl.top.setStyle('position', 'absolute');
50755             this.maskEl.top.setStyle('z-index', 10000);
50756             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50757             this.maskEl.top.setLeft(0);
50758             this.maskEl.top.setTop(0);
50759             this.maskEl.top.show();
50760             
50761             this.maskEl.left.setStyle('position', 'absolute');
50762             this.maskEl.left.setStyle('z-index', 10000);
50763             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50764             this.maskEl.left.setLeft(0);
50765             this.maskEl.left.setTop(box.y - this.padding);
50766             this.maskEl.left.show();
50767
50768             this.maskEl.bottom.setStyle('position', 'absolute');
50769             this.maskEl.bottom.setStyle('z-index', 10000);
50770             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50771             this.maskEl.bottom.setLeft(0);
50772             this.maskEl.bottom.setTop(box.bottom + this.padding);
50773             this.maskEl.bottom.show();
50774
50775             this.maskEl.right.setStyle('position', 'absolute');
50776             this.maskEl.right.setStyle('z-index', 10000);
50777             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50778             this.maskEl.right.setLeft(box.right + this.padding);
50779             this.maskEl.right.setTop(box.y - this.padding);
50780             this.maskEl.right.show();
50781
50782             this.intervalID = window.setInterval(function() {
50783                 Roo.form.BasicForm.popover.unmask();
50784             }, 10000);
50785
50786             window.onwheel = function(){ return false;};
50787             
50788             (function(){ this.isMasked = true; }).defer(500, this);
50789             
50790         },
50791         
50792         unmask : function()
50793         {
50794             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50795                 return;
50796             }
50797             
50798             this.maskEl.top.setStyle('position', 'absolute');
50799             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50800             this.maskEl.top.hide();
50801
50802             this.maskEl.left.setStyle('position', 'absolute');
50803             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50804             this.maskEl.left.hide();
50805
50806             this.maskEl.bottom.setStyle('position', 'absolute');
50807             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50808             this.maskEl.bottom.hide();
50809
50810             this.maskEl.right.setStyle('position', 'absolute');
50811             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50812             this.maskEl.right.hide();
50813             
50814             window.onwheel = function(){ return true;};
50815             
50816             if(this.intervalID){
50817                 window.clearInterval(this.intervalID);
50818                 this.intervalID = false;
50819             }
50820             
50821             this.isMasked = false;
50822             
50823         }
50824         
50825     }
50826     
50827 });/*
50828  * Based on:
50829  * Ext JS Library 1.1.1
50830  * Copyright(c) 2006-2007, Ext JS, LLC.
50831  *
50832  * Originally Released Under LGPL - original licence link has changed is not relivant.
50833  *
50834  * Fork - LGPL
50835  * <script type="text/javascript">
50836  */
50837
50838 /**
50839  * @class Roo.form.Form
50840  * @extends Roo.form.BasicForm
50841  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50842  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50843  * @constructor
50844  * @param {Object} config Configuration options
50845  */
50846 Roo.form.Form = function(config){
50847     var xitems =  [];
50848     if (config.items) {
50849         xitems = config.items;
50850         delete config.items;
50851     }
50852    
50853     
50854     Roo.form.Form.superclass.constructor.call(this, null, config);
50855     this.url = this.url || this.action;
50856     if(!this.root){
50857         this.root = new Roo.form.Layout(Roo.applyIf({
50858             id: Roo.id()
50859         }, config));
50860     }
50861     this.active = this.root;
50862     /**
50863      * Array of all the buttons that have been added to this form via {@link addButton}
50864      * @type Array
50865      */
50866     this.buttons = [];
50867     this.allItems = [];
50868     this.addEvents({
50869         /**
50870          * @event clientvalidation
50871          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50872          * @param {Form} this
50873          * @param {Boolean} valid true if the form has passed client-side validation
50874          */
50875         clientvalidation: true,
50876         /**
50877          * @event rendered
50878          * Fires when the form is rendered
50879          * @param {Roo.form.Form} form
50880          */
50881         rendered : true
50882     });
50883     
50884     if (this.progressUrl) {
50885             // push a hidden field onto the list of fields..
50886             this.addxtype( {
50887                     xns: Roo.form, 
50888                     xtype : 'Hidden', 
50889                     name : 'UPLOAD_IDENTIFIER' 
50890             });
50891         }
50892         
50893     
50894     Roo.each(xitems, this.addxtype, this);
50895     
50896 };
50897
50898 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50899      /**
50900      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50901      */
50902     
50903     /**
50904      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50905      */
50906     /**
50907      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50908      */
50909     /**
50910      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50911      */
50912     buttonAlign:'center',
50913
50914     /**
50915      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50916      */
50917     minButtonWidth:75,
50918
50919     /**
50920      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50921      * This property cascades to child containers if not set.
50922      */
50923     labelAlign:'left',
50924
50925     /**
50926      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50927      * fires a looping event with that state. This is required to bind buttons to the valid
50928      * state using the config value formBind:true on the button.
50929      */
50930     monitorValid : false,
50931
50932     /**
50933      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50934      */
50935     monitorPoll : 200,
50936     
50937     /**
50938      * @cfg {String} progressUrl - Url to return progress data 
50939      */
50940     
50941     progressUrl : false,
50942     /**
50943      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50944      * sending a formdata with extra parameters - eg uploaded elements.
50945      */
50946     
50947     formData : false,
50948     
50949     /**
50950      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50951      * fields are added and the column is closed. If no fields are passed the column remains open
50952      * until end() is called.
50953      * @param {Object} config The config to pass to the column
50954      * @param {Field} field1 (optional)
50955      * @param {Field} field2 (optional)
50956      * @param {Field} etc (optional)
50957      * @return Column The column container object
50958      */
50959     column : function(c){
50960         var col = new Roo.form.Column(c);
50961         this.start(col);
50962         if(arguments.length > 1){ // duplicate code required because of Opera
50963             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50964             this.end();
50965         }
50966         return col;
50967     },
50968
50969     /**
50970      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50971      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50972      * until end() is called.
50973      * @param {Object} config The config to pass to the fieldset
50974      * @param {Field} field1 (optional)
50975      * @param {Field} field2 (optional)
50976      * @param {Field} etc (optional)
50977      * @return FieldSet The fieldset container object
50978      */
50979     fieldset : function(c){
50980         var fs = new Roo.form.FieldSet(c);
50981         this.start(fs);
50982         if(arguments.length > 1){ // duplicate code required because of Opera
50983             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50984             this.end();
50985         }
50986         return fs;
50987     },
50988
50989     /**
50990      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50991      * fields are added and the container is closed. If no fields are passed the container remains open
50992      * until end() is called.
50993      * @param {Object} config The config to pass to the Layout
50994      * @param {Field} field1 (optional)
50995      * @param {Field} field2 (optional)
50996      * @param {Field} etc (optional)
50997      * @return Layout The container object
50998      */
50999     container : function(c){
51000         var l = new Roo.form.Layout(c);
51001         this.start(l);
51002         if(arguments.length > 1){ // duplicate code required because of Opera
51003             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51004             this.end();
51005         }
51006         return l;
51007     },
51008
51009     /**
51010      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51011      * @param {Object} container A Roo.form.Layout or subclass of Layout
51012      * @return {Form} this
51013      */
51014     start : function(c){
51015         // cascade label info
51016         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51017         this.active.stack.push(c);
51018         c.ownerCt = this.active;
51019         this.active = c;
51020         return this;
51021     },
51022
51023     /**
51024      * Closes the current open container
51025      * @return {Form} this
51026      */
51027     end : function(){
51028         if(this.active == this.root){
51029             return this;
51030         }
51031         this.active = this.active.ownerCt;
51032         return this;
51033     },
51034
51035     /**
51036      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51037      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51038      * as the label of the field.
51039      * @param {Field} field1
51040      * @param {Field} field2 (optional)
51041      * @param {Field} etc. (optional)
51042      * @return {Form} this
51043      */
51044     add : function(){
51045         this.active.stack.push.apply(this.active.stack, arguments);
51046         this.allItems.push.apply(this.allItems,arguments);
51047         var r = [];
51048         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51049             if(a[i].isFormField){
51050                 r.push(a[i]);
51051             }
51052         }
51053         if(r.length > 0){
51054             Roo.form.Form.superclass.add.apply(this, r);
51055         }
51056         return this;
51057     },
51058     
51059
51060     
51061     
51062     
51063      /**
51064      * Find any element that has been added to a form, using it's ID or name
51065      * This can include framesets, columns etc. along with regular fields..
51066      * @param {String} id - id or name to find.
51067      
51068      * @return {Element} e - or false if nothing found.
51069      */
51070     findbyId : function(id)
51071     {
51072         var ret = false;
51073         if (!id) {
51074             return ret;
51075         }
51076         Roo.each(this.allItems, function(f){
51077             if (f.id == id || f.name == id ){
51078                 ret = f;
51079                 return false;
51080             }
51081         });
51082         return ret;
51083     },
51084
51085     
51086     
51087     /**
51088      * Render this form into the passed container. This should only be called once!
51089      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51090      * @return {Form} this
51091      */
51092     render : function(ct)
51093     {
51094         
51095         
51096         
51097         ct = Roo.get(ct);
51098         var o = this.autoCreate || {
51099             tag: 'form',
51100             method : this.method || 'POST',
51101             id : this.id || Roo.id()
51102         };
51103         this.initEl(ct.createChild(o));
51104
51105         this.root.render(this.el);
51106         
51107        
51108              
51109         this.items.each(function(f){
51110             f.render('x-form-el-'+f.id);
51111         });
51112
51113         if(this.buttons.length > 0){
51114             // tables are required to maintain order and for correct IE layout
51115             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51116                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51117                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51118             }}, null, true);
51119             var tr = tb.getElementsByTagName('tr')[0];
51120             for(var i = 0, len = this.buttons.length; i < len; i++) {
51121                 var b = this.buttons[i];
51122                 var td = document.createElement('td');
51123                 td.className = 'x-form-btn-td';
51124                 b.render(tr.appendChild(td));
51125             }
51126         }
51127         if(this.monitorValid){ // initialize after render
51128             this.startMonitoring();
51129         }
51130         this.fireEvent('rendered', this);
51131         return this;
51132     },
51133
51134     /**
51135      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51136      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51137      * object or a valid Roo.DomHelper element config
51138      * @param {Function} handler The function called when the button is clicked
51139      * @param {Object} scope (optional) The scope of the handler function
51140      * @return {Roo.Button}
51141      */
51142     addButton : function(config, handler, scope){
51143         var bc = {
51144             handler: handler,
51145             scope: scope,
51146             minWidth: this.minButtonWidth,
51147             hideParent:true
51148         };
51149         if(typeof config == "string"){
51150             bc.text = config;
51151         }else{
51152             Roo.apply(bc, config);
51153         }
51154         var btn = new Roo.Button(null, bc);
51155         this.buttons.push(btn);
51156         return btn;
51157     },
51158
51159      /**
51160      * Adds a series of form elements (using the xtype property as the factory method.
51161      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51162      * @param {Object} config 
51163      */
51164     
51165     addxtype : function()
51166     {
51167         var ar = Array.prototype.slice.call(arguments, 0);
51168         var ret = false;
51169         for(var i = 0; i < ar.length; i++) {
51170             if (!ar[i]) {
51171                 continue; // skip -- if this happends something invalid got sent, we 
51172                 // should ignore it, as basically that interface element will not show up
51173                 // and that should be pretty obvious!!
51174             }
51175             
51176             if (Roo.form[ar[i].xtype]) {
51177                 ar[i].form = this;
51178                 var fe = Roo.factory(ar[i], Roo.form);
51179                 if (!ret) {
51180                     ret = fe;
51181                 }
51182                 fe.form = this;
51183                 if (fe.store) {
51184                     fe.store.form = this;
51185                 }
51186                 if (fe.isLayout) {  
51187                          
51188                     this.start(fe);
51189                     this.allItems.push(fe);
51190                     if (fe.items && fe.addxtype) {
51191                         fe.addxtype.apply(fe, fe.items);
51192                         delete fe.items;
51193                     }
51194                      this.end();
51195                     continue;
51196                 }
51197                 
51198                 
51199                  
51200                 this.add(fe);
51201               //  console.log('adding ' + ar[i].xtype);
51202             }
51203             if (ar[i].xtype == 'Button') {  
51204                 //console.log('adding button');
51205                 //console.log(ar[i]);
51206                 this.addButton(ar[i]);
51207                 this.allItems.push(fe);
51208                 continue;
51209             }
51210             
51211             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51212                 alert('end is not supported on xtype any more, use items');
51213             //    this.end();
51214             //    //console.log('adding end');
51215             }
51216             
51217         }
51218         return ret;
51219     },
51220     
51221     /**
51222      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51223      * option "monitorValid"
51224      */
51225     startMonitoring : function(){
51226         if(!this.bound){
51227             this.bound = true;
51228             Roo.TaskMgr.start({
51229                 run : this.bindHandler,
51230                 interval : this.monitorPoll || 200,
51231                 scope: this
51232             });
51233         }
51234     },
51235
51236     /**
51237      * Stops monitoring of the valid state of this form
51238      */
51239     stopMonitoring : function(){
51240         this.bound = false;
51241     },
51242
51243     // private
51244     bindHandler : function(){
51245         if(!this.bound){
51246             return false; // stops binding
51247         }
51248         var valid = true;
51249         this.items.each(function(f){
51250             if(!f.isValid(true)){
51251                 valid = false;
51252                 return false;
51253             }
51254         });
51255         for(var i = 0, len = this.buttons.length; i < len; i++){
51256             var btn = this.buttons[i];
51257             if(btn.formBind === true && btn.disabled === valid){
51258                 btn.setDisabled(!valid);
51259             }
51260         }
51261         this.fireEvent('clientvalidation', this, valid);
51262     }
51263     
51264     
51265     
51266     
51267     
51268     
51269     
51270     
51271 });
51272
51273
51274 // back compat
51275 Roo.Form = Roo.form.Form;
51276 /*
51277  * Based on:
51278  * Ext JS Library 1.1.1
51279  * Copyright(c) 2006-2007, Ext JS, LLC.
51280  *
51281  * Originally Released Under LGPL - original licence link has changed is not relivant.
51282  *
51283  * Fork - LGPL
51284  * <script type="text/javascript">
51285  */
51286
51287 // as we use this in bootstrap.
51288 Roo.namespace('Roo.form');
51289  /**
51290  * @class Roo.form.Action
51291  * Internal Class used to handle form actions
51292  * @constructor
51293  * @param {Roo.form.BasicForm} el The form element or its id
51294  * @param {Object} config Configuration options
51295  */
51296
51297  
51298  
51299 // define the action interface
51300 Roo.form.Action = function(form, options){
51301     this.form = form;
51302     this.options = options || {};
51303 };
51304 /**
51305  * Client Validation Failed
51306  * @const 
51307  */
51308 Roo.form.Action.CLIENT_INVALID = 'client';
51309 /**
51310  * Server Validation Failed
51311  * @const 
51312  */
51313 Roo.form.Action.SERVER_INVALID = 'server';
51314  /**
51315  * Connect to Server Failed
51316  * @const 
51317  */
51318 Roo.form.Action.CONNECT_FAILURE = 'connect';
51319 /**
51320  * Reading Data from Server Failed
51321  * @const 
51322  */
51323 Roo.form.Action.LOAD_FAILURE = 'load';
51324
51325 Roo.form.Action.prototype = {
51326     type : 'default',
51327     failureType : undefined,
51328     response : undefined,
51329     result : undefined,
51330
51331     // interface method
51332     run : function(options){
51333
51334     },
51335
51336     // interface method
51337     success : function(response){
51338
51339     },
51340
51341     // interface method
51342     handleResponse : function(response){
51343
51344     },
51345
51346     // default connection failure
51347     failure : function(response){
51348         
51349         this.response = response;
51350         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51351         this.form.afterAction(this, false);
51352     },
51353
51354     processResponse : function(response){
51355         this.response = response;
51356         if(!response.responseText){
51357             return true;
51358         }
51359         this.result = this.handleResponse(response);
51360         return this.result;
51361     },
51362
51363     // utility functions used internally
51364     getUrl : function(appendParams){
51365         var url = this.options.url || this.form.url || this.form.el.dom.action;
51366         if(appendParams){
51367             var p = this.getParams();
51368             if(p){
51369                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51370             }
51371         }
51372         return url;
51373     },
51374
51375     getMethod : function(){
51376         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51377     },
51378
51379     getParams : function(){
51380         var bp = this.form.baseParams;
51381         var p = this.options.params;
51382         if(p){
51383             if(typeof p == "object"){
51384                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51385             }else if(typeof p == 'string' && bp){
51386                 p += '&' + Roo.urlEncode(bp);
51387             }
51388         }else if(bp){
51389             p = Roo.urlEncode(bp);
51390         }
51391         return p;
51392     },
51393
51394     createCallback : function(){
51395         return {
51396             success: this.success,
51397             failure: this.failure,
51398             scope: this,
51399             timeout: (this.form.timeout*1000),
51400             upload: this.form.fileUpload ? this.success : undefined
51401         };
51402     }
51403 };
51404
51405 Roo.form.Action.Submit = function(form, options){
51406     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51407 };
51408
51409 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51410     type : 'submit',
51411
51412     haveProgress : false,
51413     uploadComplete : false,
51414     
51415     // uploadProgress indicator.
51416     uploadProgress : function()
51417     {
51418         if (!this.form.progressUrl) {
51419             return;
51420         }
51421         
51422         if (!this.haveProgress) {
51423             Roo.MessageBox.progress("Uploading", "Uploading");
51424         }
51425         if (this.uploadComplete) {
51426            Roo.MessageBox.hide();
51427            return;
51428         }
51429         
51430         this.haveProgress = true;
51431    
51432         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51433         
51434         var c = new Roo.data.Connection();
51435         c.request({
51436             url : this.form.progressUrl,
51437             params: {
51438                 id : uid
51439             },
51440             method: 'GET',
51441             success : function(req){
51442                //console.log(data);
51443                 var rdata = false;
51444                 var edata;
51445                 try  {
51446                    rdata = Roo.decode(req.responseText)
51447                 } catch (e) {
51448                     Roo.log("Invalid data from server..");
51449                     Roo.log(edata);
51450                     return;
51451                 }
51452                 if (!rdata || !rdata.success) {
51453                     Roo.log(rdata);
51454                     Roo.MessageBox.alert(Roo.encode(rdata));
51455                     return;
51456                 }
51457                 var data = rdata.data;
51458                 
51459                 if (this.uploadComplete) {
51460                    Roo.MessageBox.hide();
51461                    return;
51462                 }
51463                    
51464                 if (data){
51465                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51466                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51467                     );
51468                 }
51469                 this.uploadProgress.defer(2000,this);
51470             },
51471        
51472             failure: function(data) {
51473                 Roo.log('progress url failed ');
51474                 Roo.log(data);
51475             },
51476             scope : this
51477         });
51478            
51479     },
51480     
51481     
51482     run : function()
51483     {
51484         // run get Values on the form, so it syncs any secondary forms.
51485         this.form.getValues();
51486         
51487         var o = this.options;
51488         var method = this.getMethod();
51489         var isPost = method == 'POST';
51490         if(o.clientValidation === false || this.form.isValid()){
51491             
51492             if (this.form.progressUrl) {
51493                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51494                     (new Date() * 1) + '' + Math.random());
51495                     
51496             } 
51497             
51498             
51499             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51500                 form:this.form.el.dom,
51501                 url:this.getUrl(!isPost),
51502                 method: method,
51503                 params:isPost ? this.getParams() : null,
51504                 isUpload: this.form.fileUpload,
51505                 formData : this.form.formData
51506             }));
51507             
51508             this.uploadProgress();
51509
51510         }else if (o.clientValidation !== false){ // client validation failed
51511             this.failureType = Roo.form.Action.CLIENT_INVALID;
51512             this.form.afterAction(this, false);
51513         }
51514     },
51515
51516     success : function(response)
51517     {
51518         this.uploadComplete= true;
51519         if (this.haveProgress) {
51520             Roo.MessageBox.hide();
51521         }
51522         
51523         
51524         var result = this.processResponse(response);
51525         if(result === true || result.success){
51526             this.form.afterAction(this, true);
51527             return;
51528         }
51529         if(result.errors){
51530             this.form.markInvalid(result.errors);
51531             this.failureType = Roo.form.Action.SERVER_INVALID;
51532         }
51533         this.form.afterAction(this, false);
51534     },
51535     failure : function(response)
51536     {
51537         this.uploadComplete= true;
51538         if (this.haveProgress) {
51539             Roo.MessageBox.hide();
51540         }
51541         
51542         this.response = response;
51543         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51544         this.form.afterAction(this, false);
51545     },
51546     
51547     handleResponse : function(response){
51548         if(this.form.errorReader){
51549             var rs = this.form.errorReader.read(response);
51550             var errors = [];
51551             if(rs.records){
51552                 for(var i = 0, len = rs.records.length; i < len; i++) {
51553                     var r = rs.records[i];
51554                     errors[i] = r.data;
51555                 }
51556             }
51557             if(errors.length < 1){
51558                 errors = null;
51559             }
51560             return {
51561                 success : rs.success,
51562                 errors : errors
51563             };
51564         }
51565         var ret = false;
51566         try {
51567             ret = Roo.decode(response.responseText);
51568         } catch (e) {
51569             ret = {
51570                 success: false,
51571                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51572                 errors : []
51573             };
51574         }
51575         return ret;
51576         
51577     }
51578 });
51579
51580
51581 Roo.form.Action.Load = function(form, options){
51582     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51583     this.reader = this.form.reader;
51584 };
51585
51586 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51587     type : 'load',
51588
51589     run : function(){
51590         
51591         Roo.Ajax.request(Roo.apply(
51592                 this.createCallback(), {
51593                     method:this.getMethod(),
51594                     url:this.getUrl(false),
51595                     params:this.getParams()
51596         }));
51597     },
51598
51599     success : function(response){
51600         
51601         var result = this.processResponse(response);
51602         if(result === true || !result.success || !result.data){
51603             this.failureType = Roo.form.Action.LOAD_FAILURE;
51604             this.form.afterAction(this, false);
51605             return;
51606         }
51607         this.form.clearInvalid();
51608         this.form.setValues(result.data);
51609         this.form.afterAction(this, true);
51610     },
51611
51612     handleResponse : function(response){
51613         if(this.form.reader){
51614             var rs = this.form.reader.read(response);
51615             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51616             return {
51617                 success : rs.success,
51618                 data : data
51619             };
51620         }
51621         return Roo.decode(response.responseText);
51622     }
51623 });
51624
51625 Roo.form.Action.ACTION_TYPES = {
51626     'load' : Roo.form.Action.Load,
51627     'submit' : Roo.form.Action.Submit
51628 };/*
51629  * Based on:
51630  * Ext JS Library 1.1.1
51631  * Copyright(c) 2006-2007, Ext JS, LLC.
51632  *
51633  * Originally Released Under LGPL - original licence link has changed is not relivant.
51634  *
51635  * Fork - LGPL
51636  * <script type="text/javascript">
51637  */
51638  
51639 /**
51640  * @class Roo.form.Layout
51641  * @extends Roo.Component
51642  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51643  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51644  * @constructor
51645  * @param {Object} config Configuration options
51646  */
51647 Roo.form.Layout = function(config){
51648     var xitems = [];
51649     if (config.items) {
51650         xitems = config.items;
51651         delete config.items;
51652     }
51653     Roo.form.Layout.superclass.constructor.call(this, config);
51654     this.stack = [];
51655     Roo.each(xitems, this.addxtype, this);
51656      
51657 };
51658
51659 Roo.extend(Roo.form.Layout, Roo.Component, {
51660     /**
51661      * @cfg {String/Object} autoCreate
51662      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51663      */
51664     /**
51665      * @cfg {String/Object/Function} style
51666      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51667      * a function which returns such a specification.
51668      */
51669     /**
51670      * @cfg {String} labelAlign
51671      * Valid values are "left," "top" and "right" (defaults to "left")
51672      */
51673     /**
51674      * @cfg {Number} labelWidth
51675      * Fixed width in pixels of all field labels (defaults to undefined)
51676      */
51677     /**
51678      * @cfg {Boolean} clear
51679      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51680      */
51681     clear : true,
51682     /**
51683      * @cfg {String} labelSeparator
51684      * The separator to use after field labels (defaults to ':')
51685      */
51686     labelSeparator : ':',
51687     /**
51688      * @cfg {Boolean} hideLabels
51689      * True to suppress the display of field labels in this layout (defaults to false)
51690      */
51691     hideLabels : false,
51692
51693     // private
51694     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51695     
51696     isLayout : true,
51697     
51698     // private
51699     onRender : function(ct, position){
51700         if(this.el){ // from markup
51701             this.el = Roo.get(this.el);
51702         }else {  // generate
51703             var cfg = this.getAutoCreate();
51704             this.el = ct.createChild(cfg, position);
51705         }
51706         if(this.style){
51707             this.el.applyStyles(this.style);
51708         }
51709         if(this.labelAlign){
51710             this.el.addClass('x-form-label-'+this.labelAlign);
51711         }
51712         if(this.hideLabels){
51713             this.labelStyle = "display:none";
51714             this.elementStyle = "padding-left:0;";
51715         }else{
51716             if(typeof this.labelWidth == 'number'){
51717                 this.labelStyle = "width:"+this.labelWidth+"px;";
51718                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51719             }
51720             if(this.labelAlign == 'top'){
51721                 this.labelStyle = "width:auto;";
51722                 this.elementStyle = "padding-left:0;";
51723             }
51724         }
51725         var stack = this.stack;
51726         var slen = stack.length;
51727         if(slen > 0){
51728             if(!this.fieldTpl){
51729                 var t = new Roo.Template(
51730                     '<div class="x-form-item {5}">',
51731                         '<label for="{0}" style="{2}">{1}{4}</label>',
51732                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51733                         '</div>',
51734                     '</div><div class="x-form-clear-left"></div>'
51735                 );
51736                 t.disableFormats = true;
51737                 t.compile();
51738                 Roo.form.Layout.prototype.fieldTpl = t;
51739             }
51740             for(var i = 0; i < slen; i++) {
51741                 if(stack[i].isFormField){
51742                     this.renderField(stack[i]);
51743                 }else{
51744                     this.renderComponent(stack[i]);
51745                 }
51746             }
51747         }
51748         if(this.clear){
51749             this.el.createChild({cls:'x-form-clear'});
51750         }
51751     },
51752
51753     // private
51754     renderField : function(f){
51755         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51756                f.id, //0
51757                f.fieldLabel, //1
51758                f.labelStyle||this.labelStyle||'', //2
51759                this.elementStyle||'', //3
51760                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51761                f.itemCls||this.itemCls||''  //5
51762        ], true).getPrevSibling());
51763     },
51764
51765     // private
51766     renderComponent : function(c){
51767         c.render(c.isLayout ? this.el : this.el.createChild());    
51768     },
51769     /**
51770      * Adds a object form elements (using the xtype property as the factory method.)
51771      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51772      * @param {Object} config 
51773      */
51774     addxtype : function(o)
51775     {
51776         // create the lement.
51777         o.form = this.form;
51778         var fe = Roo.factory(o, Roo.form);
51779         this.form.allItems.push(fe);
51780         this.stack.push(fe);
51781         
51782         if (fe.isFormField) {
51783             this.form.items.add(fe);
51784         }
51785          
51786         return fe;
51787     }
51788 });
51789
51790 /**
51791  * @class Roo.form.Column
51792  * @extends Roo.form.Layout
51793  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51794  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51795  * @constructor
51796  * @param {Object} config Configuration options
51797  */
51798 Roo.form.Column = function(config){
51799     Roo.form.Column.superclass.constructor.call(this, config);
51800 };
51801
51802 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51803     /**
51804      * @cfg {Number/String} width
51805      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51806      */
51807     /**
51808      * @cfg {String/Object} autoCreate
51809      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51810      */
51811
51812     // private
51813     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51814
51815     // private
51816     onRender : function(ct, position){
51817         Roo.form.Column.superclass.onRender.call(this, ct, position);
51818         if(this.width){
51819             this.el.setWidth(this.width);
51820         }
51821     }
51822 });
51823
51824
51825 /**
51826  * @class Roo.form.Row
51827  * @extends Roo.form.Layout
51828  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51829  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51830  * @constructor
51831  * @param {Object} config Configuration options
51832  */
51833
51834  
51835 Roo.form.Row = function(config){
51836     Roo.form.Row.superclass.constructor.call(this, config);
51837 };
51838  
51839 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51840       /**
51841      * @cfg {Number/String} width
51842      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51843      */
51844     /**
51845      * @cfg {Number/String} height
51846      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51847      */
51848     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51849     
51850     padWidth : 20,
51851     // private
51852     onRender : function(ct, position){
51853         //console.log('row render');
51854         if(!this.rowTpl){
51855             var t = new Roo.Template(
51856                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51857                     '<label for="{0}" style="{2}">{1}{4}</label>',
51858                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51859                     '</div>',
51860                 '</div>'
51861             );
51862             t.disableFormats = true;
51863             t.compile();
51864             Roo.form.Layout.prototype.rowTpl = t;
51865         }
51866         this.fieldTpl = this.rowTpl;
51867         
51868         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51869         var labelWidth = 100;
51870         
51871         if ((this.labelAlign != 'top')) {
51872             if (typeof this.labelWidth == 'number') {
51873                 labelWidth = this.labelWidth
51874             }
51875             this.padWidth =  20 + labelWidth;
51876             
51877         }
51878         
51879         Roo.form.Column.superclass.onRender.call(this, ct, position);
51880         if(this.width){
51881             this.el.setWidth(this.width);
51882         }
51883         if(this.height){
51884             this.el.setHeight(this.height);
51885         }
51886     },
51887     
51888     // private
51889     renderField : function(f){
51890         f.fieldEl = this.fieldTpl.append(this.el, [
51891                f.id, f.fieldLabel,
51892                f.labelStyle||this.labelStyle||'',
51893                this.elementStyle||'',
51894                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51895                f.itemCls||this.itemCls||'',
51896                f.width ? f.width + this.padWidth : 160 + this.padWidth
51897        ],true);
51898     }
51899 });
51900  
51901
51902 /**
51903  * @class Roo.form.FieldSet
51904  * @extends Roo.form.Layout
51905  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51906  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51907  * @constructor
51908  * @param {Object} config Configuration options
51909  */
51910 Roo.form.FieldSet = function(config){
51911     Roo.form.FieldSet.superclass.constructor.call(this, config);
51912 };
51913
51914 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51915     /**
51916      * @cfg {String} legend
51917      * The text to display as the legend for the FieldSet (defaults to '')
51918      */
51919     /**
51920      * @cfg {String/Object} autoCreate
51921      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51922      */
51923
51924     // private
51925     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51926
51927     // private
51928     onRender : function(ct, position){
51929         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51930         if(this.legend){
51931             this.setLegend(this.legend);
51932         }
51933     },
51934
51935     // private
51936     setLegend : function(text){
51937         if(this.rendered){
51938             this.el.child('legend').update(text);
51939         }
51940     }
51941 });/*
51942  * Based on:
51943  * Ext JS Library 1.1.1
51944  * Copyright(c) 2006-2007, Ext JS, LLC.
51945  *
51946  * Originally Released Under LGPL - original licence link has changed is not relivant.
51947  *
51948  * Fork - LGPL
51949  * <script type="text/javascript">
51950  */
51951 /**
51952  * @class Roo.form.VTypes
51953  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51954  * @static
51955  */
51956 Roo.form.VTypes = function(){
51957     // closure these in so they are only created once.
51958     var alpha = /^[a-zA-Z_]+$/;
51959     var alphanum = /^[a-zA-Z0-9_]+$/;
51960     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51961     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51962
51963     // All these messages and functions are configurable
51964     return {
51965         /**
51966          * The function used to validate email addresses
51967          * @param {String} value The email address
51968          */
51969         'email' : function(v){
51970             return email.test(v);
51971         },
51972         /**
51973          * The error text to display when the email validation function returns false
51974          * @type String
51975          */
51976         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51977         /**
51978          * The keystroke filter mask to be applied on email input
51979          * @type RegExp
51980          */
51981         'emailMask' : /[a-z0-9_\.\-@]/i,
51982
51983         /**
51984          * The function used to validate URLs
51985          * @param {String} value The URL
51986          */
51987         'url' : function(v){
51988             return url.test(v);
51989         },
51990         /**
51991          * The error text to display when the url validation function returns false
51992          * @type String
51993          */
51994         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51995         
51996         /**
51997          * The function used to validate alpha values
51998          * @param {String} value The value
51999          */
52000         'alpha' : function(v){
52001             return alpha.test(v);
52002         },
52003         /**
52004          * The error text to display when the alpha validation function returns false
52005          * @type String
52006          */
52007         'alphaText' : 'This field should only contain letters and _',
52008         /**
52009          * The keystroke filter mask to be applied on alpha input
52010          * @type RegExp
52011          */
52012         'alphaMask' : /[a-z_]/i,
52013
52014         /**
52015          * The function used to validate alphanumeric values
52016          * @param {String} value The value
52017          */
52018         'alphanum' : function(v){
52019             return alphanum.test(v);
52020         },
52021         /**
52022          * The error text to display when the alphanumeric validation function returns false
52023          * @type String
52024          */
52025         'alphanumText' : 'This field should only contain letters, numbers and _',
52026         /**
52027          * The keystroke filter mask to be applied on alphanumeric input
52028          * @type RegExp
52029          */
52030         'alphanumMask' : /[a-z0-9_]/i
52031     };
52032 }();//<script type="text/javascript">
52033
52034 /**
52035  * @class Roo.form.FCKeditor
52036  * @extends Roo.form.TextArea
52037  * Wrapper around the FCKEditor http://www.fckeditor.net
52038  * @constructor
52039  * Creates a new FCKeditor
52040  * @param {Object} config Configuration options
52041  */
52042 Roo.form.FCKeditor = function(config){
52043     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52044     this.addEvents({
52045          /**
52046          * @event editorinit
52047          * Fired when the editor is initialized - you can add extra handlers here..
52048          * @param {FCKeditor} this
52049          * @param {Object} the FCK object.
52050          */
52051         editorinit : true
52052     });
52053     
52054     
52055 };
52056 Roo.form.FCKeditor.editors = { };
52057 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52058 {
52059     //defaultAutoCreate : {
52060     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52061     //},
52062     // private
52063     /**
52064      * @cfg {Object} fck options - see fck manual for details.
52065      */
52066     fckconfig : false,
52067     
52068     /**
52069      * @cfg {Object} fck toolbar set (Basic or Default)
52070      */
52071     toolbarSet : 'Basic',
52072     /**
52073      * @cfg {Object} fck BasePath
52074      */ 
52075     basePath : '/fckeditor/',
52076     
52077     
52078     frame : false,
52079     
52080     value : '',
52081     
52082    
52083     onRender : function(ct, position)
52084     {
52085         if(!this.el){
52086             this.defaultAutoCreate = {
52087                 tag: "textarea",
52088                 style:"width:300px;height:60px;",
52089                 autocomplete: "new-password"
52090             };
52091         }
52092         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52093         /*
52094         if(this.grow){
52095             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52096             if(this.preventScrollbars){
52097                 this.el.setStyle("overflow", "hidden");
52098             }
52099             this.el.setHeight(this.growMin);
52100         }
52101         */
52102         //console.log('onrender' + this.getId() );
52103         Roo.form.FCKeditor.editors[this.getId()] = this;
52104          
52105
52106         this.replaceTextarea() ;
52107         
52108     },
52109     
52110     getEditor : function() {
52111         return this.fckEditor;
52112     },
52113     /**
52114      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52115      * @param {Mixed} value The value to set
52116      */
52117     
52118     
52119     setValue : function(value)
52120     {
52121         //console.log('setValue: ' + value);
52122         
52123         if(typeof(value) == 'undefined') { // not sure why this is happending...
52124             return;
52125         }
52126         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52127         
52128         //if(!this.el || !this.getEditor()) {
52129         //    this.value = value;
52130             //this.setValue.defer(100,this,[value]);    
52131         //    return;
52132         //} 
52133         
52134         if(!this.getEditor()) {
52135             return;
52136         }
52137         
52138         this.getEditor().SetData(value);
52139         
52140         //
52141
52142     },
52143
52144     /**
52145      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52146      * @return {Mixed} value The field value
52147      */
52148     getValue : function()
52149     {
52150         
52151         if (this.frame && this.frame.dom.style.display == 'none') {
52152             return Roo.form.FCKeditor.superclass.getValue.call(this);
52153         }
52154         
52155         if(!this.el || !this.getEditor()) {
52156            
52157            // this.getValue.defer(100,this); 
52158             return this.value;
52159         }
52160        
52161         
52162         var value=this.getEditor().GetData();
52163         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52164         return Roo.form.FCKeditor.superclass.getValue.call(this);
52165         
52166
52167     },
52168
52169     /**
52170      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52171      * @return {Mixed} value The field value
52172      */
52173     getRawValue : function()
52174     {
52175         if (this.frame && this.frame.dom.style.display == 'none') {
52176             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52177         }
52178         
52179         if(!this.el || !this.getEditor()) {
52180             //this.getRawValue.defer(100,this); 
52181             return this.value;
52182             return;
52183         }
52184         
52185         
52186         
52187         var value=this.getEditor().GetData();
52188         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52189         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52190          
52191     },
52192     
52193     setSize : function(w,h) {
52194         
52195         
52196         
52197         //if (this.frame && this.frame.dom.style.display == 'none') {
52198         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52199         //    return;
52200         //}
52201         //if(!this.el || !this.getEditor()) {
52202         //    this.setSize.defer(100,this, [w,h]); 
52203         //    return;
52204         //}
52205         
52206         
52207         
52208         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52209         
52210         this.frame.dom.setAttribute('width', w);
52211         this.frame.dom.setAttribute('height', h);
52212         this.frame.setSize(w,h);
52213         
52214     },
52215     
52216     toggleSourceEdit : function(value) {
52217         
52218       
52219          
52220         this.el.dom.style.display = value ? '' : 'none';
52221         this.frame.dom.style.display = value ?  'none' : '';
52222         
52223     },
52224     
52225     
52226     focus: function(tag)
52227     {
52228         if (this.frame.dom.style.display == 'none') {
52229             return Roo.form.FCKeditor.superclass.focus.call(this);
52230         }
52231         if(!this.el || !this.getEditor()) {
52232             this.focus.defer(100,this, [tag]); 
52233             return;
52234         }
52235         
52236         
52237         
52238         
52239         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52240         this.getEditor().Focus();
52241         if (tgs.length) {
52242             if (!this.getEditor().Selection.GetSelection()) {
52243                 this.focus.defer(100,this, [tag]); 
52244                 return;
52245             }
52246             
52247             
52248             var r = this.getEditor().EditorDocument.createRange();
52249             r.setStart(tgs[0],0);
52250             r.setEnd(tgs[0],0);
52251             this.getEditor().Selection.GetSelection().removeAllRanges();
52252             this.getEditor().Selection.GetSelection().addRange(r);
52253             this.getEditor().Focus();
52254         }
52255         
52256     },
52257     
52258     
52259     
52260     replaceTextarea : function()
52261     {
52262         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52263             return ;
52264         }
52265         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52266         //{
52267             // We must check the elements firstly using the Id and then the name.
52268         var oTextarea = document.getElementById( this.getId() );
52269         
52270         var colElementsByName = document.getElementsByName( this.getId() ) ;
52271          
52272         oTextarea.style.display = 'none' ;
52273
52274         if ( oTextarea.tabIndex ) {            
52275             this.TabIndex = oTextarea.tabIndex ;
52276         }
52277         
52278         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52279         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52280         this.frame = Roo.get(this.getId() + '___Frame')
52281     },
52282     
52283     _getConfigHtml : function()
52284     {
52285         var sConfig = '' ;
52286
52287         for ( var o in this.fckconfig ) {
52288             sConfig += sConfig.length > 0  ? '&amp;' : '';
52289             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52290         }
52291
52292         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52293     },
52294     
52295     
52296     _getIFrameHtml : function()
52297     {
52298         var sFile = 'fckeditor.html' ;
52299         /* no idea what this is about..
52300         try
52301         {
52302             if ( (/fcksource=true/i).test( window.top.location.search ) )
52303                 sFile = 'fckeditor.original.html' ;
52304         }
52305         catch (e) { 
52306         */
52307
52308         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52309         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52310         
52311         
52312         var html = '<iframe id="' + this.getId() +
52313             '___Frame" src="' + sLink +
52314             '" width="' + this.width +
52315             '" height="' + this.height + '"' +
52316             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52317             ' frameborder="0" scrolling="no"></iframe>' ;
52318
52319         return html ;
52320     },
52321     
52322     _insertHtmlBefore : function( html, element )
52323     {
52324         if ( element.insertAdjacentHTML )       {
52325             // IE
52326             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52327         } else { // Gecko
52328             var oRange = document.createRange() ;
52329             oRange.setStartBefore( element ) ;
52330             var oFragment = oRange.createContextualFragment( html );
52331             element.parentNode.insertBefore( oFragment, element ) ;
52332         }
52333     }
52334     
52335     
52336   
52337     
52338     
52339     
52340     
52341
52342 });
52343
52344 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52345
52346 function FCKeditor_OnComplete(editorInstance){
52347     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52348     f.fckEditor = editorInstance;
52349     //console.log("loaded");
52350     f.fireEvent('editorinit', f, editorInstance);
52351
52352   
52353
52354  
52355
52356
52357
52358
52359
52360
52361
52362
52363
52364
52365
52366
52367
52368
52369
52370 //<script type="text/javascript">
52371 /**
52372  * @class Roo.form.GridField
52373  * @extends Roo.form.Field
52374  * Embed a grid (or editable grid into a form)
52375  * STATUS ALPHA
52376  * 
52377  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52378  * it needs 
52379  * xgrid.store = Roo.data.Store
52380  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52381  * xgrid.store.reader = Roo.data.JsonReader 
52382  * 
52383  * 
52384  * @constructor
52385  * Creates a new GridField
52386  * @param {Object} config Configuration options
52387  */
52388 Roo.form.GridField = function(config){
52389     Roo.form.GridField.superclass.constructor.call(this, config);
52390      
52391 };
52392
52393 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52394     /**
52395      * @cfg {Number} width  - used to restrict width of grid..
52396      */
52397     width : 100,
52398     /**
52399      * @cfg {Number} height - used to restrict height of grid..
52400      */
52401     height : 50,
52402      /**
52403      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52404          * 
52405          *}
52406      */
52407     xgrid : false, 
52408     /**
52409      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52410      * {tag: "input", type: "checkbox", autocomplete: "off"})
52411      */
52412    // defaultAutoCreate : { tag: 'div' },
52413     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52414     /**
52415      * @cfg {String} addTitle Text to include for adding a title.
52416      */
52417     addTitle : false,
52418     //
52419     onResize : function(){
52420         Roo.form.Field.superclass.onResize.apply(this, arguments);
52421     },
52422
52423     initEvents : function(){
52424         // Roo.form.Checkbox.superclass.initEvents.call(this);
52425         // has no events...
52426        
52427     },
52428
52429
52430     getResizeEl : function(){
52431         return this.wrap;
52432     },
52433
52434     getPositionEl : function(){
52435         return this.wrap;
52436     },
52437
52438     // private
52439     onRender : function(ct, position){
52440         
52441         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52442         var style = this.style;
52443         delete this.style;
52444         
52445         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52446         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52447         this.viewEl = this.wrap.createChild({ tag: 'div' });
52448         if (style) {
52449             this.viewEl.applyStyles(style);
52450         }
52451         if (this.width) {
52452             this.viewEl.setWidth(this.width);
52453         }
52454         if (this.height) {
52455             this.viewEl.setHeight(this.height);
52456         }
52457         //if(this.inputValue !== undefined){
52458         //this.setValue(this.value);
52459         
52460         
52461         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52462         
52463         
52464         this.grid.render();
52465         this.grid.getDataSource().on('remove', this.refreshValue, this);
52466         this.grid.getDataSource().on('update', this.refreshValue, this);
52467         this.grid.on('afteredit', this.refreshValue, this);
52468  
52469     },
52470      
52471     
52472     /**
52473      * Sets the value of the item. 
52474      * @param {String} either an object  or a string..
52475      */
52476     setValue : function(v){
52477         //this.value = v;
52478         v = v || []; // empty set..
52479         // this does not seem smart - it really only affects memoryproxy grids..
52480         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52481             var ds = this.grid.getDataSource();
52482             // assumes a json reader..
52483             var data = {}
52484             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52485             ds.loadData( data);
52486         }
52487         // clear selection so it does not get stale.
52488         if (this.grid.sm) { 
52489             this.grid.sm.clearSelections();
52490         }
52491         
52492         Roo.form.GridField.superclass.setValue.call(this, v);
52493         this.refreshValue();
52494         // should load data in the grid really....
52495     },
52496     
52497     // private
52498     refreshValue: function() {
52499          var val = [];
52500         this.grid.getDataSource().each(function(r) {
52501             val.push(r.data);
52502         });
52503         this.el.dom.value = Roo.encode(val);
52504     }
52505     
52506      
52507     
52508     
52509 });/*
52510  * Based on:
52511  * Ext JS Library 1.1.1
52512  * Copyright(c) 2006-2007, Ext JS, LLC.
52513  *
52514  * Originally Released Under LGPL - original licence link has changed is not relivant.
52515  *
52516  * Fork - LGPL
52517  * <script type="text/javascript">
52518  */
52519 /**
52520  * @class Roo.form.DisplayField
52521  * @extends Roo.form.Field
52522  * A generic Field to display non-editable data.
52523  * @cfg {Boolean} closable (true|false) default false
52524  * @constructor
52525  * Creates a new Display Field item.
52526  * @param {Object} config Configuration options
52527  */
52528 Roo.form.DisplayField = function(config){
52529     Roo.form.DisplayField.superclass.constructor.call(this, config);
52530     
52531     this.addEvents({
52532         /**
52533          * @event close
52534          * Fires after the click the close btn
52535              * @param {Roo.form.DisplayField} this
52536              */
52537         close : true
52538     });
52539 };
52540
52541 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52542     inputType:      'hidden',
52543     allowBlank:     true,
52544     readOnly:         true,
52545     
52546  
52547     /**
52548      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52549      */
52550     focusClass : undefined,
52551     /**
52552      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52553      */
52554     fieldClass: 'x-form-field',
52555     
52556      /**
52557      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52558      */
52559     valueRenderer: undefined,
52560     
52561     width: 100,
52562     /**
52563      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52564      * {tag: "input", type: "checkbox", autocomplete: "off"})
52565      */
52566      
52567  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52568  
52569     closable : false,
52570     
52571     onResize : function(){
52572         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52573         
52574     },
52575
52576     initEvents : function(){
52577         // Roo.form.Checkbox.superclass.initEvents.call(this);
52578         // has no events...
52579         
52580         if(this.closable){
52581             this.closeEl.on('click', this.onClose, this);
52582         }
52583        
52584     },
52585
52586
52587     getResizeEl : function(){
52588         return this.wrap;
52589     },
52590
52591     getPositionEl : function(){
52592         return this.wrap;
52593     },
52594
52595     // private
52596     onRender : function(ct, position){
52597         
52598         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52599         //if(this.inputValue !== undefined){
52600         this.wrap = this.el.wrap();
52601         
52602         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52603         
52604         if(this.closable){
52605             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52606         }
52607         
52608         if (this.bodyStyle) {
52609             this.viewEl.applyStyles(this.bodyStyle);
52610         }
52611         //this.viewEl.setStyle('padding', '2px');
52612         
52613         this.setValue(this.value);
52614         
52615     },
52616 /*
52617     // private
52618     initValue : Roo.emptyFn,
52619
52620   */
52621
52622         // private
52623     onClick : function(){
52624         
52625     },
52626
52627     /**
52628      * Sets the checked state of the checkbox.
52629      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52630      */
52631     setValue : function(v){
52632         this.value = v;
52633         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52634         // this might be called before we have a dom element..
52635         if (!this.viewEl) {
52636             return;
52637         }
52638         this.viewEl.dom.innerHTML = html;
52639         Roo.form.DisplayField.superclass.setValue.call(this, v);
52640
52641     },
52642     
52643     onClose : function(e)
52644     {
52645         e.preventDefault();
52646         
52647         this.fireEvent('close', this);
52648     }
52649 });/*
52650  * 
52651  * Licence- LGPL
52652  * 
52653  */
52654
52655 /**
52656  * @class Roo.form.DayPicker
52657  * @extends Roo.form.Field
52658  * A Day picker show [M] [T] [W] ....
52659  * @constructor
52660  * Creates a new Day Picker
52661  * @param {Object} config Configuration options
52662  */
52663 Roo.form.DayPicker= function(config){
52664     Roo.form.DayPicker.superclass.constructor.call(this, config);
52665      
52666 };
52667
52668 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52669     /**
52670      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52671      */
52672     focusClass : undefined,
52673     /**
52674      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52675      */
52676     fieldClass: "x-form-field",
52677    
52678     /**
52679      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52680      * {tag: "input", type: "checkbox", autocomplete: "off"})
52681      */
52682     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52683     
52684    
52685     actionMode : 'viewEl', 
52686     //
52687     // private
52688  
52689     inputType : 'hidden',
52690     
52691      
52692     inputElement: false, // real input element?
52693     basedOn: false, // ????
52694     
52695     isFormField: true, // not sure where this is needed!!!!
52696
52697     onResize : function(){
52698         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52699         if(!this.boxLabel){
52700             this.el.alignTo(this.wrap, 'c-c');
52701         }
52702     },
52703
52704     initEvents : function(){
52705         Roo.form.Checkbox.superclass.initEvents.call(this);
52706         this.el.on("click", this.onClick,  this);
52707         this.el.on("change", this.onClick,  this);
52708     },
52709
52710
52711     getResizeEl : function(){
52712         return this.wrap;
52713     },
52714
52715     getPositionEl : function(){
52716         return this.wrap;
52717     },
52718
52719     
52720     // private
52721     onRender : function(ct, position){
52722         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52723        
52724         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52725         
52726         var r1 = '<table><tr>';
52727         var r2 = '<tr class="x-form-daypick-icons">';
52728         for (var i=0; i < 7; i++) {
52729             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52730             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52731         }
52732         
52733         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52734         viewEl.select('img').on('click', this.onClick, this);
52735         this.viewEl = viewEl;   
52736         
52737         
52738         // this will not work on Chrome!!!
52739         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52740         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52741         
52742         
52743           
52744
52745     },
52746
52747     // private
52748     initValue : Roo.emptyFn,
52749
52750     /**
52751      * Returns the checked state of the checkbox.
52752      * @return {Boolean} True if checked, else false
52753      */
52754     getValue : function(){
52755         return this.el.dom.value;
52756         
52757     },
52758
52759         // private
52760     onClick : function(e){ 
52761         //this.setChecked(!this.checked);
52762         Roo.get(e.target).toggleClass('x-menu-item-checked');
52763         this.refreshValue();
52764         //if(this.el.dom.checked != this.checked){
52765         //    this.setValue(this.el.dom.checked);
52766        // }
52767     },
52768     
52769     // private
52770     refreshValue : function()
52771     {
52772         var val = '';
52773         this.viewEl.select('img',true).each(function(e,i,n)  {
52774             val += e.is(".x-menu-item-checked") ? String(n) : '';
52775         });
52776         this.setValue(val, true);
52777     },
52778
52779     /**
52780      * Sets the checked state of the checkbox.
52781      * On is always based on a string comparison between inputValue and the param.
52782      * @param {Boolean/String} value - the value to set 
52783      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52784      */
52785     setValue : function(v,suppressEvent){
52786         if (!this.el.dom) {
52787             return;
52788         }
52789         var old = this.el.dom.value ;
52790         this.el.dom.value = v;
52791         if (suppressEvent) {
52792             return ;
52793         }
52794          
52795         // update display..
52796         this.viewEl.select('img',true).each(function(e,i,n)  {
52797             
52798             var on = e.is(".x-menu-item-checked");
52799             var newv = v.indexOf(String(n)) > -1;
52800             if (on != newv) {
52801                 e.toggleClass('x-menu-item-checked');
52802             }
52803             
52804         });
52805         
52806         
52807         this.fireEvent('change', this, v, old);
52808         
52809         
52810     },
52811    
52812     // handle setting of hidden value by some other method!!?!?
52813     setFromHidden: function()
52814     {
52815         if(!this.el){
52816             return;
52817         }
52818         //console.log("SET FROM HIDDEN");
52819         //alert('setFrom hidden');
52820         this.setValue(this.el.dom.value);
52821     },
52822     
52823     onDestroy : function()
52824     {
52825         if(this.viewEl){
52826             Roo.get(this.viewEl).remove();
52827         }
52828          
52829         Roo.form.DayPicker.superclass.onDestroy.call(this);
52830     }
52831
52832 });/*
52833  * RooJS Library 1.1.1
52834  * Copyright(c) 2008-2011  Alan Knowles
52835  *
52836  * License - LGPL
52837  */
52838  
52839
52840 /**
52841  * @class Roo.form.ComboCheck
52842  * @extends Roo.form.ComboBox
52843  * A combobox for multiple select items.
52844  *
52845  * FIXME - could do with a reset button..
52846  * 
52847  * @constructor
52848  * Create a new ComboCheck
52849  * @param {Object} config Configuration options
52850  */
52851 Roo.form.ComboCheck = function(config){
52852     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52853     // should verify some data...
52854     // like
52855     // hiddenName = required..
52856     // displayField = required
52857     // valudField == required
52858     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52859     var _t = this;
52860     Roo.each(req, function(e) {
52861         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52862             throw "Roo.form.ComboCheck : missing value for: " + e;
52863         }
52864     });
52865     
52866     
52867 };
52868
52869 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52870      
52871      
52872     editable : false,
52873      
52874     selectedClass: 'x-menu-item-checked', 
52875     
52876     // private
52877     onRender : function(ct, position){
52878         var _t = this;
52879         
52880         
52881         
52882         if(!this.tpl){
52883             var cls = 'x-combo-list';
52884
52885             
52886             this.tpl =  new Roo.Template({
52887                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52888                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52889                    '<span>{' + this.displayField + '}</span>' +
52890                     '</div>' 
52891                 
52892             });
52893         }
52894  
52895         
52896         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52897         this.view.singleSelect = false;
52898         this.view.multiSelect = true;
52899         this.view.toggleSelect = true;
52900         this.pageTb.add(new Roo.Toolbar.Fill(), {
52901             
52902             text: 'Done',
52903             handler: function()
52904             {
52905                 _t.collapse();
52906             }
52907         });
52908     },
52909     
52910     onViewOver : function(e, t){
52911         // do nothing...
52912         return;
52913         
52914     },
52915     
52916     onViewClick : function(doFocus,index){
52917         return;
52918         
52919     },
52920     select: function () {
52921         //Roo.log("SELECT CALLED");
52922     },
52923      
52924     selectByValue : function(xv, scrollIntoView){
52925         var ar = this.getValueArray();
52926         var sels = [];
52927         
52928         Roo.each(ar, function(v) {
52929             if(v === undefined || v === null){
52930                 return;
52931             }
52932             var r = this.findRecord(this.valueField, v);
52933             if(r){
52934                 sels.push(this.store.indexOf(r))
52935                 
52936             }
52937         },this);
52938         this.view.select(sels);
52939         return false;
52940     },
52941     
52942     
52943     
52944     onSelect : function(record, index){
52945        // Roo.log("onselect Called");
52946        // this is only called by the clear button now..
52947         this.view.clearSelections();
52948         this.setValue('[]');
52949         if (this.value != this.valueBefore) {
52950             this.fireEvent('change', this, this.value, this.valueBefore);
52951             this.valueBefore = this.value;
52952         }
52953     },
52954     getValueArray : function()
52955     {
52956         var ar = [] ;
52957         
52958         try {
52959             //Roo.log(this.value);
52960             if (typeof(this.value) == 'undefined') {
52961                 return [];
52962             }
52963             var ar = Roo.decode(this.value);
52964             return  ar instanceof Array ? ar : []; //?? valid?
52965             
52966         } catch(e) {
52967             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52968             return [];
52969         }
52970          
52971     },
52972     expand : function ()
52973     {
52974         
52975         Roo.form.ComboCheck.superclass.expand.call(this);
52976         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52977         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52978         
52979
52980     },
52981     
52982     collapse : function(){
52983         Roo.form.ComboCheck.superclass.collapse.call(this);
52984         var sl = this.view.getSelectedIndexes();
52985         var st = this.store;
52986         var nv = [];
52987         var tv = [];
52988         var r;
52989         Roo.each(sl, function(i) {
52990             r = st.getAt(i);
52991             nv.push(r.get(this.valueField));
52992         },this);
52993         this.setValue(Roo.encode(nv));
52994         if (this.value != this.valueBefore) {
52995
52996             this.fireEvent('change', this, this.value, this.valueBefore);
52997             this.valueBefore = this.value;
52998         }
52999         
53000     },
53001     
53002     setValue : function(v){
53003         // Roo.log(v);
53004         this.value = v;
53005         
53006         var vals = this.getValueArray();
53007         var tv = [];
53008         Roo.each(vals, function(k) {
53009             var r = this.findRecord(this.valueField, k);
53010             if(r){
53011                 tv.push(r.data[this.displayField]);
53012             }else if(this.valueNotFoundText !== undefined){
53013                 tv.push( this.valueNotFoundText );
53014             }
53015         },this);
53016        // Roo.log(tv);
53017         
53018         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53019         this.hiddenField.value = v;
53020         this.value = v;
53021     }
53022     
53023 });/*
53024  * Based on:
53025  * Ext JS Library 1.1.1
53026  * Copyright(c) 2006-2007, Ext JS, LLC.
53027  *
53028  * Originally Released Under LGPL - original licence link has changed is not relivant.
53029  *
53030  * Fork - LGPL
53031  * <script type="text/javascript">
53032  */
53033  
53034 /**
53035  * @class Roo.form.Signature
53036  * @extends Roo.form.Field
53037  * Signature field.  
53038  * @constructor
53039  * 
53040  * @param {Object} config Configuration options
53041  */
53042
53043 Roo.form.Signature = function(config){
53044     Roo.form.Signature.superclass.constructor.call(this, config);
53045     
53046     this.addEvents({// not in used??
53047          /**
53048          * @event confirm
53049          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53050              * @param {Roo.form.Signature} combo This combo box
53051              */
53052         'confirm' : true,
53053         /**
53054          * @event reset
53055          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53056              * @param {Roo.form.ComboBox} combo This combo box
53057              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53058              */
53059         'reset' : true
53060     });
53061 };
53062
53063 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53064     /**
53065      * @cfg {Object} labels Label to use when rendering a form.
53066      * defaults to 
53067      * labels : { 
53068      *      clear : "Clear",
53069      *      confirm : "Confirm"
53070      *  }
53071      */
53072     labels : { 
53073         clear : "Clear",
53074         confirm : "Confirm"
53075     },
53076     /**
53077      * @cfg {Number} width The signature panel width (defaults to 300)
53078      */
53079     width: 300,
53080     /**
53081      * @cfg {Number} height The signature panel height (defaults to 100)
53082      */
53083     height : 100,
53084     /**
53085      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53086      */
53087     allowBlank : false,
53088     
53089     //private
53090     // {Object} signPanel The signature SVG panel element (defaults to {})
53091     signPanel : {},
53092     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53093     isMouseDown : false,
53094     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53095     isConfirmed : false,
53096     // {String} signatureTmp SVG mapping string (defaults to empty string)
53097     signatureTmp : '',
53098     
53099     
53100     defaultAutoCreate : { // modified by initCompnoent..
53101         tag: "input",
53102         type:"hidden"
53103     },
53104
53105     // private
53106     onRender : function(ct, position){
53107         
53108         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53109         
53110         this.wrap = this.el.wrap({
53111             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53112         });
53113         
53114         this.createToolbar(this);
53115         this.signPanel = this.wrap.createChild({
53116                 tag: 'div',
53117                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53118             }, this.el
53119         );
53120             
53121         this.svgID = Roo.id();
53122         this.svgEl = this.signPanel.createChild({
53123               xmlns : 'http://www.w3.org/2000/svg',
53124               tag : 'svg',
53125               id : this.svgID + "-svg",
53126               width: this.width,
53127               height: this.height,
53128               viewBox: '0 0 '+this.width+' '+this.height,
53129               cn : [
53130                 {
53131                     tag: "rect",
53132                     id: this.svgID + "-svg-r",
53133                     width: this.width,
53134                     height: this.height,
53135                     fill: "#ffa"
53136                 },
53137                 {
53138                     tag: "line",
53139                     id: this.svgID + "-svg-l",
53140                     x1: "0", // start
53141                     y1: (this.height*0.8), // start set the line in 80% of height
53142                     x2: this.width, // end
53143                     y2: (this.height*0.8), // end set the line in 80% of height
53144                     'stroke': "#666",
53145                     'stroke-width': "1",
53146                     'stroke-dasharray': "3",
53147                     'shape-rendering': "crispEdges",
53148                     'pointer-events': "none"
53149                 },
53150                 {
53151                     tag: "path",
53152                     id: this.svgID + "-svg-p",
53153                     'stroke': "navy",
53154                     'stroke-width': "3",
53155                     'fill': "none",
53156                     'pointer-events': 'none'
53157                 }
53158               ]
53159         });
53160         this.createSVG();
53161         this.svgBox = this.svgEl.dom.getScreenCTM();
53162     },
53163     createSVG : function(){ 
53164         var svg = this.signPanel;
53165         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53166         var t = this;
53167
53168         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53169         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53170         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53171         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53172         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53173         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53174         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53175         
53176     },
53177     isTouchEvent : function(e){
53178         return e.type.match(/^touch/);
53179     },
53180     getCoords : function (e) {
53181         var pt    = this.svgEl.dom.createSVGPoint();
53182         pt.x = e.clientX; 
53183         pt.y = e.clientY;
53184         if (this.isTouchEvent(e)) {
53185             pt.x =  e.targetTouches[0].clientX;
53186             pt.y = e.targetTouches[0].clientY;
53187         }
53188         var a = this.svgEl.dom.getScreenCTM();
53189         var b = a.inverse();
53190         var mx = pt.matrixTransform(b);
53191         return mx.x + ',' + mx.y;
53192     },
53193     //mouse event headler 
53194     down : function (e) {
53195         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53196         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53197         
53198         this.isMouseDown = true;
53199         
53200         e.preventDefault();
53201     },
53202     move : function (e) {
53203         if (this.isMouseDown) {
53204             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53205             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53206         }
53207         
53208         e.preventDefault();
53209     },
53210     up : function (e) {
53211         this.isMouseDown = false;
53212         var sp = this.signatureTmp.split(' ');
53213         
53214         if(sp.length > 1){
53215             if(!sp[sp.length-2].match(/^L/)){
53216                 sp.pop();
53217                 sp.pop();
53218                 sp.push("");
53219                 this.signatureTmp = sp.join(" ");
53220             }
53221         }
53222         if(this.getValue() != this.signatureTmp){
53223             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53224             this.isConfirmed = false;
53225         }
53226         e.preventDefault();
53227     },
53228     
53229     /**
53230      * Protected method that will not generally be called directly. It
53231      * is called when the editor creates its toolbar. Override this method if you need to
53232      * add custom toolbar buttons.
53233      * @param {HtmlEditor} editor
53234      */
53235     createToolbar : function(editor){
53236          function btn(id, toggle, handler){
53237             var xid = fid + '-'+ id ;
53238             return {
53239                 id : xid,
53240                 cmd : id,
53241                 cls : 'x-btn-icon x-edit-'+id,
53242                 enableToggle:toggle !== false,
53243                 scope: editor, // was editor...
53244                 handler:handler||editor.relayBtnCmd,
53245                 clickEvent:'mousedown',
53246                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53247                 tabIndex:-1
53248             };
53249         }
53250         
53251         
53252         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53253         this.tb = tb;
53254         this.tb.add(
53255            {
53256                 cls : ' x-signature-btn x-signature-'+id,
53257                 scope: editor, // was editor...
53258                 handler: this.reset,
53259                 clickEvent:'mousedown',
53260                 text: this.labels.clear
53261             },
53262             {
53263                  xtype : 'Fill',
53264                  xns: Roo.Toolbar
53265             }, 
53266             {
53267                 cls : '  x-signature-btn x-signature-'+id,
53268                 scope: editor, // was editor...
53269                 handler: this.confirmHandler,
53270                 clickEvent:'mousedown',
53271                 text: this.labels.confirm
53272             }
53273         );
53274     
53275     },
53276     //public
53277     /**
53278      * when user is clicked confirm then show this image.....
53279      * 
53280      * @return {String} Image Data URI
53281      */
53282     getImageDataURI : function(){
53283         var svg = this.svgEl.dom.parentNode.innerHTML;
53284         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53285         return src; 
53286     },
53287     /**
53288      * 
53289      * @return {Boolean} this.isConfirmed
53290      */
53291     getConfirmed : function(){
53292         return this.isConfirmed;
53293     },
53294     /**
53295      * 
53296      * @return {Number} this.width
53297      */
53298     getWidth : function(){
53299         return this.width;
53300     },
53301     /**
53302      * 
53303      * @return {Number} this.height
53304      */
53305     getHeight : function(){
53306         return this.height;
53307     },
53308     // private
53309     getSignature : function(){
53310         return this.signatureTmp;
53311     },
53312     // private
53313     reset : function(){
53314         this.signatureTmp = '';
53315         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53316         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53317         this.isConfirmed = false;
53318         Roo.form.Signature.superclass.reset.call(this);
53319     },
53320     setSignature : function(s){
53321         this.signatureTmp = s;
53322         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53323         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53324         this.setValue(s);
53325         this.isConfirmed = false;
53326         Roo.form.Signature.superclass.reset.call(this);
53327     }, 
53328     test : function(){
53329 //        Roo.log(this.signPanel.dom.contentWindow.up())
53330     },
53331     //private
53332     setConfirmed : function(){
53333         
53334         
53335         
53336 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53337     },
53338     // private
53339     confirmHandler : function(){
53340         if(!this.getSignature()){
53341             return;
53342         }
53343         
53344         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53345         this.setValue(this.getSignature());
53346         this.isConfirmed = true;
53347         
53348         this.fireEvent('confirm', this);
53349     },
53350     // private
53351     // Subclasses should provide the validation implementation by overriding this
53352     validateValue : function(value){
53353         if(this.allowBlank){
53354             return true;
53355         }
53356         
53357         if(this.isConfirmed){
53358             return true;
53359         }
53360         return false;
53361     }
53362 });/*
53363  * Based on:
53364  * Ext JS Library 1.1.1
53365  * Copyright(c) 2006-2007, Ext JS, LLC.
53366  *
53367  * Originally Released Under LGPL - original licence link has changed is not relivant.
53368  *
53369  * Fork - LGPL
53370  * <script type="text/javascript">
53371  */
53372  
53373
53374 /**
53375  * @class Roo.form.ComboBox
53376  * @extends Roo.form.TriggerField
53377  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53378  * @constructor
53379  * Create a new ComboBox.
53380  * @param {Object} config Configuration options
53381  */
53382 Roo.form.Select = function(config){
53383     Roo.form.Select.superclass.constructor.call(this, config);
53384      
53385 };
53386
53387 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53388     /**
53389      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53390      */
53391     /**
53392      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53393      * rendering into an Roo.Editor, defaults to false)
53394      */
53395     /**
53396      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53397      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53398      */
53399     /**
53400      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53401      */
53402     /**
53403      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53404      * the dropdown list (defaults to undefined, with no header element)
53405      */
53406
53407      /**
53408      * @cfg {String/Roo.Template} tpl The template to use to render the output
53409      */
53410      
53411     // private
53412     defaultAutoCreate : {tag: "select"  },
53413     /**
53414      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53415      */
53416     listWidth: undefined,
53417     /**
53418      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53419      * mode = 'remote' or 'text' if mode = 'local')
53420      */
53421     displayField: undefined,
53422     /**
53423      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53424      * mode = 'remote' or 'value' if mode = 'local'). 
53425      * Note: use of a valueField requires the user make a selection
53426      * in order for a value to be mapped.
53427      */
53428     valueField: undefined,
53429     
53430     
53431     /**
53432      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53433      * field's data value (defaults to the underlying DOM element's name)
53434      */
53435     hiddenName: undefined,
53436     /**
53437      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53438      */
53439     listClass: '',
53440     /**
53441      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53442      */
53443     selectedClass: 'x-combo-selected',
53444     /**
53445      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53446      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53447      * which displays a downward arrow icon).
53448      */
53449     triggerClass : 'x-form-arrow-trigger',
53450     /**
53451      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53452      */
53453     shadow:'sides',
53454     /**
53455      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53456      * anchor positions (defaults to 'tl-bl')
53457      */
53458     listAlign: 'tl-bl?',
53459     /**
53460      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53461      */
53462     maxHeight: 300,
53463     /**
53464      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53465      * query specified by the allQuery config option (defaults to 'query')
53466      */
53467     triggerAction: 'query',
53468     /**
53469      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53470      * (defaults to 4, does not apply if editable = false)
53471      */
53472     minChars : 4,
53473     /**
53474      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53475      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53476      */
53477     typeAhead: false,
53478     /**
53479      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53480      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53481      */
53482     queryDelay: 500,
53483     /**
53484      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53485      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53486      */
53487     pageSize: 0,
53488     /**
53489      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53490      * when editable = true (defaults to false)
53491      */
53492     selectOnFocus:false,
53493     /**
53494      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53495      */
53496     queryParam: 'query',
53497     /**
53498      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53499      * when mode = 'remote' (defaults to 'Loading...')
53500      */
53501     loadingText: 'Loading...',
53502     /**
53503      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53504      */
53505     resizable: false,
53506     /**
53507      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53508      */
53509     handleHeight : 8,
53510     /**
53511      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53512      * traditional select (defaults to true)
53513      */
53514     editable: true,
53515     /**
53516      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53517      */
53518     allQuery: '',
53519     /**
53520      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53521      */
53522     mode: 'remote',
53523     /**
53524      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53525      * listWidth has a higher value)
53526      */
53527     minListWidth : 70,
53528     /**
53529      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53530      * allow the user to set arbitrary text into the field (defaults to false)
53531      */
53532     forceSelection:false,
53533     /**
53534      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53535      * if typeAhead = true (defaults to 250)
53536      */
53537     typeAheadDelay : 250,
53538     /**
53539      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53540      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53541      */
53542     valueNotFoundText : undefined,
53543     
53544     /**
53545      * @cfg {String} defaultValue The value displayed after loading the store.
53546      */
53547     defaultValue: '',
53548     
53549     /**
53550      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53551      */
53552     blockFocus : false,
53553     
53554     /**
53555      * @cfg {Boolean} disableClear Disable showing of clear button.
53556      */
53557     disableClear : false,
53558     /**
53559      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53560      */
53561     alwaysQuery : false,
53562     
53563     //private
53564     addicon : false,
53565     editicon: false,
53566     
53567     // element that contains real text value.. (when hidden is used..)
53568      
53569     // private
53570     onRender : function(ct, position){
53571         Roo.form.Field.prototype.onRender.call(this, ct, position);
53572         
53573         if(this.store){
53574             this.store.on('beforeload', this.onBeforeLoad, this);
53575             this.store.on('load', this.onLoad, this);
53576             this.store.on('loadexception', this.onLoadException, this);
53577             this.store.load({});
53578         }
53579         
53580         
53581         
53582     },
53583
53584     // private
53585     initEvents : function(){
53586         //Roo.form.ComboBox.superclass.initEvents.call(this);
53587  
53588     },
53589
53590     onDestroy : function(){
53591        
53592         if(this.store){
53593             this.store.un('beforeload', this.onBeforeLoad, this);
53594             this.store.un('load', this.onLoad, this);
53595             this.store.un('loadexception', this.onLoadException, this);
53596         }
53597         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53598     },
53599
53600     // private
53601     fireKey : function(e){
53602         if(e.isNavKeyPress() && !this.list.isVisible()){
53603             this.fireEvent("specialkey", this, e);
53604         }
53605     },
53606
53607     // private
53608     onResize: function(w, h){
53609         
53610         return; 
53611     
53612         
53613     },
53614
53615     /**
53616      * Allow or prevent the user from directly editing the field text.  If false is passed,
53617      * the user will only be able to select from the items defined in the dropdown list.  This method
53618      * is the runtime equivalent of setting the 'editable' config option at config time.
53619      * @param {Boolean} value True to allow the user to directly edit the field text
53620      */
53621     setEditable : function(value){
53622          
53623     },
53624
53625     // private
53626     onBeforeLoad : function(){
53627         
53628         Roo.log("Select before load");
53629         return;
53630     
53631         this.innerList.update(this.loadingText ?
53632                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53633         //this.restrictHeight();
53634         this.selectedIndex = -1;
53635     },
53636
53637     // private
53638     onLoad : function(){
53639
53640     
53641         var dom = this.el.dom;
53642         dom.innerHTML = '';
53643          var od = dom.ownerDocument;
53644          
53645         if (this.emptyText) {
53646             var op = od.createElement('option');
53647             op.setAttribute('value', '');
53648             op.innerHTML = String.format('{0}', this.emptyText);
53649             dom.appendChild(op);
53650         }
53651         if(this.store.getCount() > 0){
53652            
53653             var vf = this.valueField;
53654             var df = this.displayField;
53655             this.store.data.each(function(r) {
53656                 // which colmsn to use... testing - cdoe / title..
53657                 var op = od.createElement('option');
53658                 op.setAttribute('value', r.data[vf]);
53659                 op.innerHTML = String.format('{0}', r.data[df]);
53660                 dom.appendChild(op);
53661             });
53662             if (typeof(this.defaultValue != 'undefined')) {
53663                 this.setValue(this.defaultValue);
53664             }
53665             
53666              
53667         }else{
53668             //this.onEmptyResults();
53669         }
53670         //this.el.focus();
53671     },
53672     // private
53673     onLoadException : function()
53674     {
53675         dom.innerHTML = '';
53676             
53677         Roo.log("Select on load exception");
53678         return;
53679     
53680         this.collapse();
53681         Roo.log(this.store.reader.jsonData);
53682         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53683             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53684         }
53685         
53686         
53687     },
53688     // private
53689     onTypeAhead : function(){
53690          
53691     },
53692
53693     // private
53694     onSelect : function(record, index){
53695         Roo.log('on select?');
53696         return;
53697         if(this.fireEvent('beforeselect', this, record, index) !== false){
53698             this.setFromData(index > -1 ? record.data : false);
53699             this.collapse();
53700             this.fireEvent('select', this, record, index);
53701         }
53702     },
53703
53704     /**
53705      * Returns the currently selected field value or empty string if no value is set.
53706      * @return {String} value The selected value
53707      */
53708     getValue : function(){
53709         var dom = this.el.dom;
53710         this.value = dom.options[dom.selectedIndex].value;
53711         return this.value;
53712         
53713     },
53714
53715     /**
53716      * Clears any text/value currently set in the field
53717      */
53718     clearValue : function(){
53719         this.value = '';
53720         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53721         
53722     },
53723
53724     /**
53725      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53726      * will be displayed in the field.  If the value does not match the data value of an existing item,
53727      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53728      * Otherwise the field will be blank (although the value will still be set).
53729      * @param {String} value The value to match
53730      */
53731     setValue : function(v){
53732         var d = this.el.dom;
53733         for (var i =0; i < d.options.length;i++) {
53734             if (v == d.options[i].value) {
53735                 d.selectedIndex = i;
53736                 this.value = v;
53737                 return;
53738             }
53739         }
53740         this.clearValue();
53741     },
53742     /**
53743      * @property {Object} the last set data for the element
53744      */
53745     
53746     lastData : false,
53747     /**
53748      * Sets the value of the field based on a object which is related to the record format for the store.
53749      * @param {Object} value the value to set as. or false on reset?
53750      */
53751     setFromData : function(o){
53752         Roo.log('setfrom data?');
53753          
53754         
53755         
53756     },
53757     // private
53758     reset : function(){
53759         this.clearValue();
53760     },
53761     // private
53762     findRecord : function(prop, value){
53763         
53764         return false;
53765     
53766         var record;
53767         if(this.store.getCount() > 0){
53768             this.store.each(function(r){
53769                 if(r.data[prop] == value){
53770                     record = r;
53771                     return false;
53772                 }
53773                 return true;
53774             });
53775         }
53776         return record;
53777     },
53778     
53779     getName: function()
53780     {
53781         // returns hidden if it's set..
53782         if (!this.rendered) {return ''};
53783         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53784         
53785     },
53786      
53787
53788     
53789
53790     // private
53791     onEmptyResults : function(){
53792         Roo.log('empty results');
53793         //this.collapse();
53794     },
53795
53796     /**
53797      * Returns true if the dropdown list is expanded, else false.
53798      */
53799     isExpanded : function(){
53800         return false;
53801     },
53802
53803     /**
53804      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53805      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53806      * @param {String} value The data value of the item to select
53807      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53808      * selected item if it is not currently in view (defaults to true)
53809      * @return {Boolean} True if the value matched an item in the list, else false
53810      */
53811     selectByValue : function(v, scrollIntoView){
53812         Roo.log('select By Value');
53813         return false;
53814     
53815         if(v !== undefined && v !== null){
53816             var r = this.findRecord(this.valueField || this.displayField, v);
53817             if(r){
53818                 this.select(this.store.indexOf(r), scrollIntoView);
53819                 return true;
53820             }
53821         }
53822         return false;
53823     },
53824
53825     /**
53826      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53827      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53828      * @param {Number} index The zero-based index of the list item to select
53829      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53830      * selected item if it is not currently in view (defaults to true)
53831      */
53832     select : function(index, scrollIntoView){
53833         Roo.log('select ');
53834         return  ;
53835         
53836         this.selectedIndex = index;
53837         this.view.select(index);
53838         if(scrollIntoView !== false){
53839             var el = this.view.getNode(index);
53840             if(el){
53841                 this.innerList.scrollChildIntoView(el, false);
53842             }
53843         }
53844     },
53845
53846       
53847
53848     // private
53849     validateBlur : function(){
53850         
53851         return;
53852         
53853     },
53854
53855     // private
53856     initQuery : function(){
53857         this.doQuery(this.getRawValue());
53858     },
53859
53860     // private
53861     doForce : function(){
53862         if(this.el.dom.value.length > 0){
53863             this.el.dom.value =
53864                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53865              
53866         }
53867     },
53868
53869     /**
53870      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53871      * query allowing the query action to be canceled if needed.
53872      * @param {String} query The SQL query to execute
53873      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53874      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53875      * saved in the current store (defaults to false)
53876      */
53877     doQuery : function(q, forceAll){
53878         
53879         Roo.log('doQuery?');
53880         if(q === undefined || q === null){
53881             q = '';
53882         }
53883         var qe = {
53884             query: q,
53885             forceAll: forceAll,
53886             combo: this,
53887             cancel:false
53888         };
53889         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53890             return false;
53891         }
53892         q = qe.query;
53893         forceAll = qe.forceAll;
53894         if(forceAll === true || (q.length >= this.minChars)){
53895             if(this.lastQuery != q || this.alwaysQuery){
53896                 this.lastQuery = q;
53897                 if(this.mode == 'local'){
53898                     this.selectedIndex = -1;
53899                     if(forceAll){
53900                         this.store.clearFilter();
53901                     }else{
53902                         this.store.filter(this.displayField, q);
53903                     }
53904                     this.onLoad();
53905                 }else{
53906                     this.store.baseParams[this.queryParam] = q;
53907                     this.store.load({
53908                         params: this.getParams(q)
53909                     });
53910                     this.expand();
53911                 }
53912             }else{
53913                 this.selectedIndex = -1;
53914                 this.onLoad();   
53915             }
53916         }
53917     },
53918
53919     // private
53920     getParams : function(q){
53921         var p = {};
53922         //p[this.queryParam] = q;
53923         if(this.pageSize){
53924             p.start = 0;
53925             p.limit = this.pageSize;
53926         }
53927         return p;
53928     },
53929
53930     /**
53931      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53932      */
53933     collapse : function(){
53934         
53935     },
53936
53937     // private
53938     collapseIf : function(e){
53939         
53940     },
53941
53942     /**
53943      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53944      */
53945     expand : function(){
53946         
53947     } ,
53948
53949     // private
53950      
53951
53952     /** 
53953     * @cfg {Boolean} grow 
53954     * @hide 
53955     */
53956     /** 
53957     * @cfg {Number} growMin 
53958     * @hide 
53959     */
53960     /** 
53961     * @cfg {Number} growMax 
53962     * @hide 
53963     */
53964     /**
53965      * @hide
53966      * @method autoSize
53967      */
53968     
53969     setWidth : function()
53970     {
53971         
53972     },
53973     getResizeEl : function(){
53974         return this.el;
53975     }
53976 });//<script type="text/javasscript">
53977  
53978
53979 /**
53980  * @class Roo.DDView
53981  * A DnD enabled version of Roo.View.
53982  * @param {Element/String} container The Element in which to create the View.
53983  * @param {String} tpl The template string used to create the markup for each element of the View
53984  * @param {Object} config The configuration properties. These include all the config options of
53985  * {@link Roo.View} plus some specific to this class.<br>
53986  * <p>
53987  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53988  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53989  * <p>
53990  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53991 .x-view-drag-insert-above {
53992         border-top:1px dotted #3366cc;
53993 }
53994 .x-view-drag-insert-below {
53995         border-bottom:1px dotted #3366cc;
53996 }
53997 </code></pre>
53998  * 
53999  */
54000  
54001 Roo.DDView = function(container, tpl, config) {
54002     Roo.DDView.superclass.constructor.apply(this, arguments);
54003     this.getEl().setStyle("outline", "0px none");
54004     this.getEl().unselectable();
54005     if (this.dragGroup) {
54006         this.setDraggable(this.dragGroup.split(","));
54007     }
54008     if (this.dropGroup) {
54009         this.setDroppable(this.dropGroup.split(","));
54010     }
54011     if (this.deletable) {
54012         this.setDeletable();
54013     }
54014     this.isDirtyFlag = false;
54015         this.addEvents({
54016                 "drop" : true
54017         });
54018 };
54019
54020 Roo.extend(Roo.DDView, Roo.View, {
54021 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54022 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54023 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54024 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54025
54026         isFormField: true,
54027
54028         reset: Roo.emptyFn,
54029         
54030         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54031
54032         validate: function() {
54033                 return true;
54034         },
54035         
54036         destroy: function() {
54037                 this.purgeListeners();
54038                 this.getEl.removeAllListeners();
54039                 this.getEl().remove();
54040                 if (this.dragZone) {
54041                         if (this.dragZone.destroy) {
54042                                 this.dragZone.destroy();
54043                         }
54044                 }
54045                 if (this.dropZone) {
54046                         if (this.dropZone.destroy) {
54047                                 this.dropZone.destroy();
54048                         }
54049                 }
54050         },
54051
54052 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54053         getName: function() {
54054                 return this.name;
54055         },
54056
54057 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54058         setValue: function(v) {
54059                 if (!this.store) {
54060                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54061                 }
54062                 var data = {};
54063                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54064                 this.store.proxy = new Roo.data.MemoryProxy(data);
54065                 this.store.load();
54066         },
54067
54068 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54069         getValue: function() {
54070                 var result = '(';
54071                 this.store.each(function(rec) {
54072                         result += rec.id + ',';
54073                 });
54074                 return result.substr(0, result.length - 1) + ')';
54075         },
54076         
54077         getIds: function() {
54078                 var i = 0, result = new Array(this.store.getCount());
54079                 this.store.each(function(rec) {
54080                         result[i++] = rec.id;
54081                 });
54082                 return result;
54083         },
54084         
54085         isDirty: function() {
54086                 return this.isDirtyFlag;
54087         },
54088
54089 /**
54090  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54091  *      whole Element becomes the target, and this causes the drop gesture to append.
54092  */
54093     getTargetFromEvent : function(e) {
54094                 var target = e.getTarget();
54095                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54096                 target = target.parentNode;
54097                 }
54098                 if (!target) {
54099                         target = this.el.dom.lastChild || this.el.dom;
54100                 }
54101                 return target;
54102     },
54103
54104 /**
54105  *      Create the drag data which consists of an object which has the property "ddel" as
54106  *      the drag proxy element. 
54107  */
54108     getDragData : function(e) {
54109         var target = this.findItemFromChild(e.getTarget());
54110                 if(target) {
54111                         this.handleSelection(e);
54112                         var selNodes = this.getSelectedNodes();
54113             var dragData = {
54114                 source: this,
54115                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54116                 nodes: selNodes,
54117                 records: []
54118                         };
54119                         var selectedIndices = this.getSelectedIndexes();
54120                         for (var i = 0; i < selectedIndices.length; i++) {
54121                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54122                         }
54123                         if (selNodes.length == 1) {
54124                                 dragData.ddel = target.cloneNode(true); // the div element
54125                         } else {
54126                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54127                                 div.className = 'multi-proxy';
54128                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54129                                         div.appendChild(selNodes[i].cloneNode(true));
54130                                 }
54131                                 dragData.ddel = div;
54132                         }
54133             //console.log(dragData)
54134             //console.log(dragData.ddel.innerHTML)
54135                         return dragData;
54136                 }
54137         //console.log('nodragData')
54138                 return false;
54139     },
54140     
54141 /**     Specify to which ddGroup items in this DDView may be dragged. */
54142     setDraggable: function(ddGroup) {
54143         if (ddGroup instanceof Array) {
54144                 Roo.each(ddGroup, this.setDraggable, this);
54145                 return;
54146         }
54147         if (this.dragZone) {
54148                 this.dragZone.addToGroup(ddGroup);
54149         } else {
54150                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54151                                 containerScroll: true,
54152                                 ddGroup: ddGroup 
54153
54154                         });
54155 //                      Draggability implies selection. DragZone's mousedown selects the element.
54156                         if (!this.multiSelect) { this.singleSelect = true; }
54157
54158 //                      Wire the DragZone's handlers up to methods in *this*
54159                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54160                 }
54161     },
54162
54163 /**     Specify from which ddGroup this DDView accepts drops. */
54164     setDroppable: function(ddGroup) {
54165         if (ddGroup instanceof Array) {
54166                 Roo.each(ddGroup, this.setDroppable, this);
54167                 return;
54168         }
54169         if (this.dropZone) {
54170                 this.dropZone.addToGroup(ddGroup);
54171         } else {
54172                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54173                                 containerScroll: true,
54174                                 ddGroup: ddGroup
54175                         });
54176
54177 //                      Wire the DropZone's handlers up to methods in *this*
54178                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54179                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54180                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54181                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54182                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54183                 }
54184     },
54185
54186 /**     Decide whether to drop above or below a View node. */
54187     getDropPoint : function(e, n, dd){
54188         if (n == this.el.dom) { return "above"; }
54189                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54190                 var c = t + (b - t) / 2;
54191                 var y = Roo.lib.Event.getPageY(e);
54192                 if(y <= c) {
54193                         return "above";
54194                 }else{
54195                         return "below";
54196                 }
54197     },
54198
54199     onNodeEnter : function(n, dd, e, data){
54200                 return false;
54201     },
54202     
54203     onNodeOver : function(n, dd, e, data){
54204                 var pt = this.getDropPoint(e, n, dd);
54205                 // set the insert point style on the target node
54206                 var dragElClass = this.dropNotAllowed;
54207                 if (pt) {
54208                         var targetElClass;
54209                         if (pt == "above"){
54210                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54211                                 targetElClass = "x-view-drag-insert-above";
54212                         } else {
54213                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54214                                 targetElClass = "x-view-drag-insert-below";
54215                         }
54216                         if (this.lastInsertClass != targetElClass){
54217                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54218                                 this.lastInsertClass = targetElClass;
54219                         }
54220                 }
54221                 return dragElClass;
54222         },
54223
54224     onNodeOut : function(n, dd, e, data){
54225                 this.removeDropIndicators(n);
54226     },
54227
54228     onNodeDrop : function(n, dd, e, data){
54229         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54230                 return false;
54231         }
54232         var pt = this.getDropPoint(e, n, dd);
54233                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54234                 if (pt == "below") { insertAt++; }
54235                 for (var i = 0; i < data.records.length; i++) {
54236                         var r = data.records[i];
54237                         var dup = this.store.getById(r.id);
54238                         if (dup && (dd != this.dragZone)) {
54239                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54240                         } else {
54241                                 if (data.copy) {
54242                                         this.store.insert(insertAt++, r.copy());
54243                                 } else {
54244                                         data.source.isDirtyFlag = true;
54245                                         r.store.remove(r);
54246                                         this.store.insert(insertAt++, r);
54247                                 }
54248                                 this.isDirtyFlag = true;
54249                         }
54250                 }
54251                 this.dragZone.cachedTarget = null;
54252                 return true;
54253     },
54254
54255     removeDropIndicators : function(n){
54256                 if(n){
54257                         Roo.fly(n).removeClass([
54258                                 "x-view-drag-insert-above",
54259                                 "x-view-drag-insert-below"]);
54260                         this.lastInsertClass = "_noclass";
54261                 }
54262     },
54263
54264 /**
54265  *      Utility method. Add a delete option to the DDView's context menu.
54266  *      @param {String} imageUrl The URL of the "delete" icon image.
54267  */
54268         setDeletable: function(imageUrl) {
54269                 if (!this.singleSelect && !this.multiSelect) {
54270                         this.singleSelect = true;
54271                 }
54272                 var c = this.getContextMenu();
54273                 this.contextMenu.on("itemclick", function(item) {
54274                         switch (item.id) {
54275                                 case "delete":
54276                                         this.remove(this.getSelectedIndexes());
54277                                         break;
54278                         }
54279                 }, this);
54280                 this.contextMenu.add({
54281                         icon: imageUrl,
54282                         id: "delete",
54283                         text: 'Delete'
54284                 });
54285         },
54286         
54287 /**     Return the context menu for this DDView. */
54288         getContextMenu: function() {
54289                 if (!this.contextMenu) {
54290 //                      Create the View's context menu
54291                         this.contextMenu = new Roo.menu.Menu({
54292                                 id: this.id + "-contextmenu"
54293                         });
54294                         this.el.on("contextmenu", this.showContextMenu, this);
54295                 }
54296                 return this.contextMenu;
54297         },
54298         
54299         disableContextMenu: function() {
54300                 if (this.contextMenu) {
54301                         this.el.un("contextmenu", this.showContextMenu, this);
54302                 }
54303         },
54304
54305         showContextMenu: function(e, item) {
54306         item = this.findItemFromChild(e.getTarget());
54307                 if (item) {
54308                         e.stopEvent();
54309                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54310                         this.contextMenu.showAt(e.getXY());
54311             }
54312     },
54313
54314 /**
54315  *      Remove {@link Roo.data.Record}s at the specified indices.
54316  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54317  */
54318     remove: function(selectedIndices) {
54319                 selectedIndices = [].concat(selectedIndices);
54320                 for (var i = 0; i < selectedIndices.length; i++) {
54321                         var rec = this.store.getAt(selectedIndices[i]);
54322                         this.store.remove(rec);
54323                 }
54324     },
54325
54326 /**
54327  *      Double click fires the event, but also, if this is draggable, and there is only one other
54328  *      related DropZone, it transfers the selected node.
54329  */
54330     onDblClick : function(e){
54331         var item = this.findItemFromChild(e.getTarget());
54332         if(item){
54333             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54334                 return false;
54335             }
54336             if (this.dragGroup) {
54337                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54338                     while (targets.indexOf(this.dropZone) > -1) {
54339                             targets.remove(this.dropZone);
54340                                 }
54341                     if (targets.length == 1) {
54342                                         this.dragZone.cachedTarget = null;
54343                         var el = Roo.get(targets[0].getEl());
54344                         var box = el.getBox(true);
54345                         targets[0].onNodeDrop(el.dom, {
54346                                 target: el.dom,
54347                                 xy: [box.x, box.y + box.height - 1]
54348                         }, null, this.getDragData(e));
54349                     }
54350                 }
54351         }
54352     },
54353     
54354     handleSelection: function(e) {
54355                 this.dragZone.cachedTarget = null;
54356         var item = this.findItemFromChild(e.getTarget());
54357         if (!item) {
54358                 this.clearSelections(true);
54359                 return;
54360         }
54361                 if (item && (this.multiSelect || this.singleSelect)){
54362                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54363                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54364                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54365                                 this.unselect(item);
54366                         } else {
54367                                 this.select(item, this.multiSelect && e.ctrlKey);
54368                                 this.lastSelection = item;
54369                         }
54370                 }
54371     },
54372
54373     onItemClick : function(item, index, e){
54374                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54375                         return false;
54376                 }
54377                 return true;
54378     },
54379
54380     unselect : function(nodeInfo, suppressEvent){
54381                 var node = this.getNode(nodeInfo);
54382                 if(node && this.isSelected(node)){
54383                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54384                                 Roo.fly(node).removeClass(this.selectedClass);
54385                                 this.selections.remove(node);
54386                                 if(!suppressEvent){
54387                                         this.fireEvent("selectionchange", this, this.selections);
54388                                 }
54389                         }
54390                 }
54391     }
54392 });
54393 /*
54394  * Based on:
54395  * Ext JS Library 1.1.1
54396  * Copyright(c) 2006-2007, Ext JS, LLC.
54397  *
54398  * Originally Released Under LGPL - original licence link has changed is not relivant.
54399  *
54400  * Fork - LGPL
54401  * <script type="text/javascript">
54402  */
54403  
54404 /**
54405  * @class Roo.LayoutManager
54406  * @extends Roo.util.Observable
54407  * Base class for layout managers.
54408  */
54409 Roo.LayoutManager = function(container, config){
54410     Roo.LayoutManager.superclass.constructor.call(this);
54411     this.el = Roo.get(container);
54412     // ie scrollbar fix
54413     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54414         document.body.scroll = "no";
54415     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54416         this.el.position('relative');
54417     }
54418     this.id = this.el.id;
54419     this.el.addClass("x-layout-container");
54420     /** false to disable window resize monitoring @type Boolean */
54421     this.monitorWindowResize = true;
54422     this.regions = {};
54423     this.addEvents({
54424         /**
54425          * @event layout
54426          * Fires when a layout is performed. 
54427          * @param {Roo.LayoutManager} this
54428          */
54429         "layout" : true,
54430         /**
54431          * @event regionresized
54432          * Fires when the user resizes a region. 
54433          * @param {Roo.LayoutRegion} region The resized region
54434          * @param {Number} newSize The new size (width for east/west, height for north/south)
54435          */
54436         "regionresized" : true,
54437         /**
54438          * @event regioncollapsed
54439          * Fires when a region is collapsed. 
54440          * @param {Roo.LayoutRegion} region The collapsed region
54441          */
54442         "regioncollapsed" : true,
54443         /**
54444          * @event regionexpanded
54445          * Fires when a region is expanded.  
54446          * @param {Roo.LayoutRegion} region The expanded region
54447          */
54448         "regionexpanded" : true
54449     });
54450     this.updating = false;
54451     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54452 };
54453
54454 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54455     /**
54456      * Returns true if this layout is currently being updated
54457      * @return {Boolean}
54458      */
54459     isUpdating : function(){
54460         return this.updating; 
54461     },
54462     
54463     /**
54464      * Suspend the LayoutManager from doing auto-layouts while
54465      * making multiple add or remove calls
54466      */
54467     beginUpdate : function(){
54468         this.updating = true;    
54469     },
54470     
54471     /**
54472      * Restore auto-layouts and optionally disable the manager from performing a layout
54473      * @param {Boolean} noLayout true to disable a layout update 
54474      */
54475     endUpdate : function(noLayout){
54476         this.updating = false;
54477         if(!noLayout){
54478             this.layout();
54479         }    
54480     },
54481     
54482     layout: function(){
54483         
54484     },
54485     
54486     onRegionResized : function(region, newSize){
54487         this.fireEvent("regionresized", region, newSize);
54488         this.layout();
54489     },
54490     
54491     onRegionCollapsed : function(region){
54492         this.fireEvent("regioncollapsed", region);
54493     },
54494     
54495     onRegionExpanded : function(region){
54496         this.fireEvent("regionexpanded", region);
54497     },
54498         
54499     /**
54500      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54501      * performs box-model adjustments.
54502      * @return {Object} The size as an object {width: (the width), height: (the height)}
54503      */
54504     getViewSize : function(){
54505         var size;
54506         if(this.el.dom != document.body){
54507             size = this.el.getSize();
54508         }else{
54509             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54510         }
54511         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54512         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54513         return size;
54514     },
54515     
54516     /**
54517      * Returns the Element this layout is bound to.
54518      * @return {Roo.Element}
54519      */
54520     getEl : function(){
54521         return this.el;
54522     },
54523     
54524     /**
54525      * Returns the specified region.
54526      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54527      * @return {Roo.LayoutRegion}
54528      */
54529     getRegion : function(target){
54530         return this.regions[target.toLowerCase()];
54531     },
54532     
54533     onWindowResize : function(){
54534         if(this.monitorWindowResize){
54535             this.layout();
54536         }
54537     }
54538 });/*
54539  * Based on:
54540  * Ext JS Library 1.1.1
54541  * Copyright(c) 2006-2007, Ext JS, LLC.
54542  *
54543  * Originally Released Under LGPL - original licence link has changed is not relivant.
54544  *
54545  * Fork - LGPL
54546  * <script type="text/javascript">
54547  */
54548 /**
54549  * @class Roo.BorderLayout
54550  * @extends Roo.LayoutManager
54551  * @children Roo.ContentPanel
54552  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54553  * please see: <br><br>
54554  * <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>
54555  * <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>
54556  * Example:
54557  <pre><code>
54558  var layout = new Roo.BorderLayout(document.body, {
54559     north: {
54560         initialSize: 25,
54561         titlebar: false
54562     },
54563     west: {
54564         split:true,
54565         initialSize: 200,
54566         minSize: 175,
54567         maxSize: 400,
54568         titlebar: true,
54569         collapsible: true
54570     },
54571     east: {
54572         split:true,
54573         initialSize: 202,
54574         minSize: 175,
54575         maxSize: 400,
54576         titlebar: true,
54577         collapsible: true
54578     },
54579     south: {
54580         split:true,
54581         initialSize: 100,
54582         minSize: 100,
54583         maxSize: 200,
54584         titlebar: true,
54585         collapsible: true
54586     },
54587     center: {
54588         titlebar: true,
54589         autoScroll:true,
54590         resizeTabs: true,
54591         minTabWidth: 50,
54592         preferredTabWidth: 150
54593     }
54594 });
54595
54596 // shorthand
54597 var CP = Roo.ContentPanel;
54598
54599 layout.beginUpdate();
54600 layout.add("north", new CP("north", "North"));
54601 layout.add("south", new CP("south", {title: "South", closable: true}));
54602 layout.add("west", new CP("west", {title: "West"}));
54603 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54604 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54605 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54606 layout.getRegion("center").showPanel("center1");
54607 layout.endUpdate();
54608 </code></pre>
54609
54610 <b>The container the layout is rendered into can be either the body element or any other element.
54611 If it is not the body element, the container needs to either be an absolute positioned element,
54612 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54613 the container size if it is not the body element.</b>
54614
54615 * @constructor
54616 * Create a new BorderLayout
54617 * @param {String/HTMLElement/Element} container The container this layout is bound to
54618 * @param {Object} config Configuration options
54619  */
54620 Roo.BorderLayout = function(container, config){
54621     config = config || {};
54622     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54623     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54624     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54625         var target = this.factory.validRegions[i];
54626         if(config[target]){
54627             this.addRegion(target, config[target]);
54628         }
54629     }
54630 };
54631
54632 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54633         
54634         /**
54635          * @cfg {Roo.LayoutRegion} east
54636          */
54637         /**
54638          * @cfg {Roo.LayoutRegion} west
54639          */
54640         /**
54641          * @cfg {Roo.LayoutRegion} north
54642          */
54643         /**
54644          * @cfg {Roo.LayoutRegion} south
54645          */
54646         /**
54647          * @cfg {Roo.LayoutRegion} center
54648          */
54649     /**
54650      * Creates and adds a new region if it doesn't already exist.
54651      * @param {String} target The target region key (north, south, east, west or center).
54652      * @param {Object} config The regions config object
54653      * @return {BorderLayoutRegion} The new region
54654      */
54655     addRegion : function(target, config){
54656         if(!this.regions[target]){
54657             var r = this.factory.create(target, this, config);
54658             this.bindRegion(target, r);
54659         }
54660         return this.regions[target];
54661     },
54662
54663     // private (kinda)
54664     bindRegion : function(name, r){
54665         this.regions[name] = r;
54666         r.on("visibilitychange", this.layout, this);
54667         r.on("paneladded", this.layout, this);
54668         r.on("panelremoved", this.layout, this);
54669         r.on("invalidated", this.layout, this);
54670         r.on("resized", this.onRegionResized, this);
54671         r.on("collapsed", this.onRegionCollapsed, this);
54672         r.on("expanded", this.onRegionExpanded, this);
54673     },
54674
54675     /**
54676      * Performs a layout update.
54677      */
54678     layout : function(){
54679         if(this.updating) {
54680             return;
54681         }
54682         var size = this.getViewSize();
54683         var w = size.width;
54684         var h = size.height;
54685         var centerW = w;
54686         var centerH = h;
54687         var centerY = 0;
54688         var centerX = 0;
54689         //var x = 0, y = 0;
54690
54691         var rs = this.regions;
54692         var north = rs["north"];
54693         var south = rs["south"]; 
54694         var west = rs["west"];
54695         var east = rs["east"];
54696         var center = rs["center"];
54697         //if(this.hideOnLayout){ // not supported anymore
54698             //c.el.setStyle("display", "none");
54699         //}
54700         if(north && north.isVisible()){
54701             var b = north.getBox();
54702             var m = north.getMargins();
54703             b.width = w - (m.left+m.right);
54704             b.x = m.left;
54705             b.y = m.top;
54706             centerY = b.height + b.y + m.bottom;
54707             centerH -= centerY;
54708             north.updateBox(this.safeBox(b));
54709         }
54710         if(south && south.isVisible()){
54711             var b = south.getBox();
54712             var m = south.getMargins();
54713             b.width = w - (m.left+m.right);
54714             b.x = m.left;
54715             var totalHeight = (b.height + m.top + m.bottom);
54716             b.y = h - totalHeight + m.top;
54717             centerH -= totalHeight;
54718             south.updateBox(this.safeBox(b));
54719         }
54720         if(west && west.isVisible()){
54721             var b = west.getBox();
54722             var m = west.getMargins();
54723             b.height = centerH - (m.top+m.bottom);
54724             b.x = m.left;
54725             b.y = centerY + m.top;
54726             var totalWidth = (b.width + m.left + m.right);
54727             centerX += totalWidth;
54728             centerW -= totalWidth;
54729             west.updateBox(this.safeBox(b));
54730         }
54731         if(east && east.isVisible()){
54732             var b = east.getBox();
54733             var m = east.getMargins();
54734             b.height = centerH - (m.top+m.bottom);
54735             var totalWidth = (b.width + m.left + m.right);
54736             b.x = w - totalWidth + m.left;
54737             b.y = centerY + m.top;
54738             centerW -= totalWidth;
54739             east.updateBox(this.safeBox(b));
54740         }
54741         if(center){
54742             var m = center.getMargins();
54743             var centerBox = {
54744                 x: centerX + m.left,
54745                 y: centerY + m.top,
54746                 width: centerW - (m.left+m.right),
54747                 height: centerH - (m.top+m.bottom)
54748             };
54749             //if(this.hideOnLayout){
54750                 //center.el.setStyle("display", "block");
54751             //}
54752             center.updateBox(this.safeBox(centerBox));
54753         }
54754         this.el.repaint();
54755         this.fireEvent("layout", this);
54756     },
54757
54758     // private
54759     safeBox : function(box){
54760         box.width = Math.max(0, box.width);
54761         box.height = Math.max(0, box.height);
54762         return box;
54763     },
54764
54765     /**
54766      * Adds a ContentPanel (or subclass) to this layout.
54767      * @param {String} target The target region key (north, south, east, west or center).
54768      * @param {Roo.ContentPanel} panel The panel to add
54769      * @return {Roo.ContentPanel} The added panel
54770      */
54771     add : function(target, panel){
54772          
54773         target = target.toLowerCase();
54774         return this.regions[target].add(panel);
54775     },
54776
54777     /**
54778      * Remove a ContentPanel (or subclass) to this layout.
54779      * @param {String} target The target region key (north, south, east, west or center).
54780      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54781      * @return {Roo.ContentPanel} The removed panel
54782      */
54783     remove : function(target, panel){
54784         target = target.toLowerCase();
54785         return this.regions[target].remove(panel);
54786     },
54787
54788     /**
54789      * Searches all regions for a panel with the specified id
54790      * @param {String} panelId
54791      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54792      */
54793     findPanel : function(panelId){
54794         var rs = this.regions;
54795         for(var target in rs){
54796             if(typeof rs[target] != "function"){
54797                 var p = rs[target].getPanel(panelId);
54798                 if(p){
54799                     return p;
54800                 }
54801             }
54802         }
54803         return null;
54804     },
54805
54806     /**
54807      * Searches all regions for a panel with the specified id and activates (shows) it.
54808      * @param {String/ContentPanel} panelId The panels id or the panel itself
54809      * @return {Roo.ContentPanel} The shown panel or null
54810      */
54811     showPanel : function(panelId) {
54812       var rs = this.regions;
54813       for(var target in rs){
54814          var r = rs[target];
54815          if(typeof r != "function"){
54816             if(r.hasPanel(panelId)){
54817                return r.showPanel(panelId);
54818             }
54819          }
54820       }
54821       return null;
54822    },
54823
54824    /**
54825      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54826      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54827      */
54828     restoreState : function(provider){
54829         if(!provider){
54830             provider = Roo.state.Manager;
54831         }
54832         var sm = new Roo.LayoutStateManager();
54833         sm.init(this, provider);
54834     },
54835
54836     /**
54837      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54838      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54839      * a valid ContentPanel config object.  Example:
54840      * <pre><code>
54841 // Create the main layout
54842 var layout = new Roo.BorderLayout('main-ct', {
54843     west: {
54844         split:true,
54845         minSize: 175,
54846         titlebar: true
54847     },
54848     center: {
54849         title:'Components'
54850     }
54851 }, 'main-ct');
54852
54853 // Create and add multiple ContentPanels at once via configs
54854 layout.batchAdd({
54855    west: {
54856        id: 'source-files',
54857        autoCreate:true,
54858        title:'Ext Source Files',
54859        autoScroll:true,
54860        fitToFrame:true
54861    },
54862    center : {
54863        el: cview,
54864        autoScroll:true,
54865        fitToFrame:true,
54866        toolbar: tb,
54867        resizeEl:'cbody'
54868    }
54869 });
54870 </code></pre>
54871      * @param {Object} regions An object containing ContentPanel configs by region name
54872      */
54873     batchAdd : function(regions){
54874         this.beginUpdate();
54875         for(var rname in regions){
54876             var lr = this.regions[rname];
54877             if(lr){
54878                 this.addTypedPanels(lr, regions[rname]);
54879             }
54880         }
54881         this.endUpdate();
54882     },
54883
54884     // private
54885     addTypedPanels : function(lr, ps){
54886         if(typeof ps == 'string'){
54887             lr.add(new Roo.ContentPanel(ps));
54888         }
54889         else if(ps instanceof Array){
54890             for(var i =0, len = ps.length; i < len; i++){
54891                 this.addTypedPanels(lr, ps[i]);
54892             }
54893         }
54894         else if(!ps.events){ // raw config?
54895             var el = ps.el;
54896             delete ps.el; // prevent conflict
54897             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54898         }
54899         else {  // panel object assumed!
54900             lr.add(ps);
54901         }
54902     },
54903     /**
54904      * Adds a xtype elements to the layout.
54905      * <pre><code>
54906
54907 layout.addxtype({
54908        xtype : 'ContentPanel',
54909        region: 'west',
54910        items: [ .... ]
54911    }
54912 );
54913
54914 layout.addxtype({
54915         xtype : 'NestedLayoutPanel',
54916         region: 'west',
54917         layout: {
54918            center: { },
54919            west: { }   
54920         },
54921         items : [ ... list of content panels or nested layout panels.. ]
54922    }
54923 );
54924 </code></pre>
54925      * @param {Object} cfg Xtype definition of item to add.
54926      */
54927     addxtype : function(cfg)
54928     {
54929         // basically accepts a pannel...
54930         // can accept a layout region..!?!?
54931         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54932         
54933         if (!cfg.xtype.match(/Panel$/)) {
54934             return false;
54935         }
54936         var ret = false;
54937         
54938         if (typeof(cfg.region) == 'undefined') {
54939             Roo.log("Failed to add Panel, region was not set");
54940             Roo.log(cfg);
54941             return false;
54942         }
54943         var region = cfg.region;
54944         delete cfg.region;
54945         
54946           
54947         var xitems = [];
54948         if (cfg.items) {
54949             xitems = cfg.items;
54950             delete cfg.items;
54951         }
54952         var nb = false;
54953         
54954         switch(cfg.xtype) 
54955         {
54956             case 'ContentPanel':  // ContentPanel (el, cfg)
54957             case 'ScrollPanel':  // ContentPanel (el, cfg)
54958             case 'ViewPanel': 
54959                 if(cfg.autoCreate) {
54960                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54961                 } else {
54962                     var el = this.el.createChild();
54963                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54964                 }
54965                 
54966                 this.add(region, ret);
54967                 break;
54968             
54969             
54970             case 'TreePanel': // our new panel!
54971                 cfg.el = this.el.createChild();
54972                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54973                 this.add(region, ret);
54974                 break;
54975             
54976             case 'NestedLayoutPanel': 
54977                 // create a new Layout (which is  a Border Layout...
54978                 var el = this.el.createChild();
54979                 var clayout = cfg.layout;
54980                 delete cfg.layout;
54981                 clayout.items   = clayout.items  || [];
54982                 // replace this exitems with the clayout ones..
54983                 xitems = clayout.items;
54984                  
54985                 
54986                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54987                     cfg.background = false;
54988                 }
54989                 var layout = new Roo.BorderLayout(el, clayout);
54990                 
54991                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54992                 //console.log('adding nested layout panel '  + cfg.toSource());
54993                 this.add(region, ret);
54994                 nb = {}; /// find first...
54995                 break;
54996                 
54997             case 'GridPanel': 
54998             
54999                 // needs grid and region
55000                 
55001                 //var el = this.getRegion(region).el.createChild();
55002                 var el = this.el.createChild();
55003                 // create the grid first...
55004                 
55005                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55006                 delete cfg.grid;
55007                 if (region == 'center' && this.active ) {
55008                     cfg.background = false;
55009                 }
55010                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55011                 
55012                 this.add(region, ret);
55013                 if (cfg.background) {
55014                     ret.on('activate', function(gp) {
55015                         if (!gp.grid.rendered) {
55016                             gp.grid.render();
55017                         }
55018                     });
55019                 } else {
55020                     grid.render();
55021                 }
55022                 break;
55023            
55024            
55025            
55026                 
55027                 
55028                 
55029             default:
55030                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55031                     
55032                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55033                     this.add(region, ret);
55034                 } else {
55035                 
55036                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55037                     return null;
55038                 }
55039                 
55040              // GridPanel (grid, cfg)
55041             
55042         }
55043         this.beginUpdate();
55044         // add children..
55045         var region = '';
55046         var abn = {};
55047         Roo.each(xitems, function(i)  {
55048             region = nb && i.region ? i.region : false;
55049             
55050             var add = ret.addxtype(i);
55051            
55052             if (region) {
55053                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55054                 if (!i.background) {
55055                     abn[region] = nb[region] ;
55056                 }
55057             }
55058             
55059         });
55060         this.endUpdate();
55061
55062         // make the last non-background panel active..
55063         //if (nb) { Roo.log(abn); }
55064         if (nb) {
55065             
55066             for(var r in abn) {
55067                 region = this.getRegion(r);
55068                 if (region) {
55069                     // tried using nb[r], but it does not work..
55070                      
55071                     region.showPanel(abn[r]);
55072                    
55073                 }
55074             }
55075         }
55076         return ret;
55077         
55078     }
55079 });
55080
55081 /**
55082  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55083  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55084  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55085  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55086  * <pre><code>
55087 // shorthand
55088 var CP = Roo.ContentPanel;
55089
55090 var layout = Roo.BorderLayout.create({
55091     north: {
55092         initialSize: 25,
55093         titlebar: false,
55094         panels: [new CP("north", "North")]
55095     },
55096     west: {
55097         split:true,
55098         initialSize: 200,
55099         minSize: 175,
55100         maxSize: 400,
55101         titlebar: true,
55102         collapsible: true,
55103         panels: [new CP("west", {title: "West"})]
55104     },
55105     east: {
55106         split:true,
55107         initialSize: 202,
55108         minSize: 175,
55109         maxSize: 400,
55110         titlebar: true,
55111         collapsible: true,
55112         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55113     },
55114     south: {
55115         split:true,
55116         initialSize: 100,
55117         minSize: 100,
55118         maxSize: 200,
55119         titlebar: true,
55120         collapsible: true,
55121         panels: [new CP("south", {title: "South", closable: true})]
55122     },
55123     center: {
55124         titlebar: true,
55125         autoScroll:true,
55126         resizeTabs: true,
55127         minTabWidth: 50,
55128         preferredTabWidth: 150,
55129         panels: [
55130             new CP("center1", {title: "Close Me", closable: true}),
55131             new CP("center2", {title: "Center Panel", closable: false})
55132         ]
55133     }
55134 }, document.body);
55135
55136 layout.getRegion("center").showPanel("center1");
55137 </code></pre>
55138  * @param config
55139  * @param targetEl
55140  */
55141 Roo.BorderLayout.create = function(config, targetEl){
55142     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55143     layout.beginUpdate();
55144     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55145     for(var j = 0, jlen = regions.length; j < jlen; j++){
55146         var lr = regions[j];
55147         if(layout.regions[lr] && config[lr].panels){
55148             var r = layout.regions[lr];
55149             var ps = config[lr].panels;
55150             layout.addTypedPanels(r, ps);
55151         }
55152     }
55153     layout.endUpdate();
55154     return layout;
55155 };
55156
55157 // private
55158 Roo.BorderLayout.RegionFactory = {
55159     // private
55160     validRegions : ["north","south","east","west","center"],
55161
55162     // private
55163     create : function(target, mgr, config){
55164         target = target.toLowerCase();
55165         if(config.lightweight || config.basic){
55166             return new Roo.BasicLayoutRegion(mgr, config, target);
55167         }
55168         switch(target){
55169             case "north":
55170                 return new Roo.NorthLayoutRegion(mgr, config);
55171             case "south":
55172                 return new Roo.SouthLayoutRegion(mgr, config);
55173             case "east":
55174                 return new Roo.EastLayoutRegion(mgr, config);
55175             case "west":
55176                 return new Roo.WestLayoutRegion(mgr, config);
55177             case "center":
55178                 return new Roo.CenterLayoutRegion(mgr, config);
55179         }
55180         throw 'Layout region "'+target+'" not supported.';
55181     }
55182 };/*
55183  * Based on:
55184  * Ext JS Library 1.1.1
55185  * Copyright(c) 2006-2007, Ext JS, LLC.
55186  *
55187  * Originally Released Under LGPL - original licence link has changed is not relivant.
55188  *
55189  * Fork - LGPL
55190  * <script type="text/javascript">
55191  */
55192  
55193 /**
55194  * @class Roo.BasicLayoutRegion
55195  * @extends Roo.util.Observable
55196  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55197  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55198  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55199  */
55200 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55201     this.mgr = mgr;
55202     this.position  = pos;
55203     this.events = {
55204         /**
55205          * @scope Roo.BasicLayoutRegion
55206          */
55207         
55208         /**
55209          * @event beforeremove
55210          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55211          * @param {Roo.LayoutRegion} this
55212          * @param {Roo.ContentPanel} panel The panel
55213          * @param {Object} e The cancel event object
55214          */
55215         "beforeremove" : true,
55216         /**
55217          * @event invalidated
55218          * Fires when the layout for this region is changed.
55219          * @param {Roo.LayoutRegion} this
55220          */
55221         "invalidated" : true,
55222         /**
55223          * @event visibilitychange
55224          * Fires when this region is shown or hidden 
55225          * @param {Roo.LayoutRegion} this
55226          * @param {Boolean} visibility true or false
55227          */
55228         "visibilitychange" : true,
55229         /**
55230          * @event paneladded
55231          * Fires when a panel is added. 
55232          * @param {Roo.LayoutRegion} this
55233          * @param {Roo.ContentPanel} panel The panel
55234          */
55235         "paneladded" : true,
55236         /**
55237          * @event panelremoved
55238          * Fires when a panel is removed. 
55239          * @param {Roo.LayoutRegion} this
55240          * @param {Roo.ContentPanel} panel The panel
55241          */
55242         "panelremoved" : true,
55243         /**
55244          * @event beforecollapse
55245          * Fires when this region before collapse.
55246          * @param {Roo.LayoutRegion} this
55247          */
55248         "beforecollapse" : true,
55249         /**
55250          * @event collapsed
55251          * Fires when this region is collapsed.
55252          * @param {Roo.LayoutRegion} this
55253          */
55254         "collapsed" : true,
55255         /**
55256          * @event expanded
55257          * Fires when this region is expanded.
55258          * @param {Roo.LayoutRegion} this
55259          */
55260         "expanded" : true,
55261         /**
55262          * @event slideshow
55263          * Fires when this region is slid into view.
55264          * @param {Roo.LayoutRegion} this
55265          */
55266         "slideshow" : true,
55267         /**
55268          * @event slidehide
55269          * Fires when this region slides out of view. 
55270          * @param {Roo.LayoutRegion} this
55271          */
55272         "slidehide" : true,
55273         /**
55274          * @event panelactivated
55275          * Fires when a panel is activated. 
55276          * @param {Roo.LayoutRegion} this
55277          * @param {Roo.ContentPanel} panel The activated panel
55278          */
55279         "panelactivated" : true,
55280         /**
55281          * @event resized
55282          * Fires when the user resizes this region. 
55283          * @param {Roo.LayoutRegion} this
55284          * @param {Number} newSize The new size (width for east/west, height for north/south)
55285          */
55286         "resized" : true
55287     };
55288     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55289     this.panels = new Roo.util.MixedCollection();
55290     this.panels.getKey = this.getPanelId.createDelegate(this);
55291     this.box = null;
55292     this.activePanel = null;
55293     // ensure listeners are added...
55294     
55295     if (config.listeners || config.events) {
55296         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55297             listeners : config.listeners || {},
55298             events : config.events || {}
55299         });
55300     }
55301     
55302     if(skipConfig !== true){
55303         this.applyConfig(config);
55304     }
55305 };
55306
55307 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55308     getPanelId : function(p){
55309         return p.getId();
55310     },
55311     
55312     applyConfig : function(config){
55313         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55314         this.config = config;
55315         
55316     },
55317     
55318     /**
55319      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55320      * the width, for horizontal (north, south) the height.
55321      * @param {Number} newSize The new width or height
55322      */
55323     resizeTo : function(newSize){
55324         var el = this.el ? this.el :
55325                  (this.activePanel ? this.activePanel.getEl() : null);
55326         if(el){
55327             switch(this.position){
55328                 case "east":
55329                 case "west":
55330                     el.setWidth(newSize);
55331                     this.fireEvent("resized", this, newSize);
55332                 break;
55333                 case "north":
55334                 case "south":
55335                     el.setHeight(newSize);
55336                     this.fireEvent("resized", this, newSize);
55337                 break;                
55338             }
55339         }
55340     },
55341     
55342     getBox : function(){
55343         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55344     },
55345     
55346     getMargins : function(){
55347         return this.margins;
55348     },
55349     
55350     updateBox : function(box){
55351         this.box = box;
55352         var el = this.activePanel.getEl();
55353         el.dom.style.left = box.x + "px";
55354         el.dom.style.top = box.y + "px";
55355         this.activePanel.setSize(box.width, box.height);
55356     },
55357     
55358     /**
55359      * Returns the container element for this region.
55360      * @return {Roo.Element}
55361      */
55362     getEl : function(){
55363         return this.activePanel;
55364     },
55365     
55366     /**
55367      * Returns true if this region is currently visible.
55368      * @return {Boolean}
55369      */
55370     isVisible : function(){
55371         return this.activePanel ? true : false;
55372     },
55373     
55374     setActivePanel : function(panel){
55375         panel = this.getPanel(panel);
55376         if(this.activePanel && this.activePanel != panel){
55377             this.activePanel.setActiveState(false);
55378             this.activePanel.getEl().setLeftTop(-10000,-10000);
55379         }
55380         this.activePanel = panel;
55381         panel.setActiveState(true);
55382         if(this.box){
55383             panel.setSize(this.box.width, this.box.height);
55384         }
55385         this.fireEvent("panelactivated", this, panel);
55386         this.fireEvent("invalidated");
55387     },
55388     
55389     /**
55390      * Show the specified panel.
55391      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55392      * @return {Roo.ContentPanel} The shown panel or null
55393      */
55394     showPanel : function(panel){
55395         if(panel = this.getPanel(panel)){
55396             this.setActivePanel(panel);
55397         }
55398         return panel;
55399     },
55400     
55401     /**
55402      * Get the active panel for this region.
55403      * @return {Roo.ContentPanel} The active panel or null
55404      */
55405     getActivePanel : function(){
55406         return this.activePanel;
55407     },
55408     
55409     /**
55410      * Add the passed ContentPanel(s)
55411      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55412      * @return {Roo.ContentPanel} The panel added (if only one was added)
55413      */
55414     add : function(panel){
55415         if(arguments.length > 1){
55416             for(var i = 0, len = arguments.length; i < len; i++) {
55417                 this.add(arguments[i]);
55418             }
55419             return null;
55420         }
55421         if(this.hasPanel(panel)){
55422             this.showPanel(panel);
55423             return panel;
55424         }
55425         var el = panel.getEl();
55426         if(el.dom.parentNode != this.mgr.el.dom){
55427             this.mgr.el.dom.appendChild(el.dom);
55428         }
55429         if(panel.setRegion){
55430             panel.setRegion(this);
55431         }
55432         this.panels.add(panel);
55433         el.setStyle("position", "absolute");
55434         if(!panel.background){
55435             this.setActivePanel(panel);
55436             if(this.config.initialSize && this.panels.getCount()==1){
55437                 this.resizeTo(this.config.initialSize);
55438             }
55439         }
55440         this.fireEvent("paneladded", this, panel);
55441         return panel;
55442     },
55443     
55444     /**
55445      * Returns true if the panel is in this region.
55446      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55447      * @return {Boolean}
55448      */
55449     hasPanel : function(panel){
55450         if(typeof panel == "object"){ // must be panel obj
55451             panel = panel.getId();
55452         }
55453         return this.getPanel(panel) ? true : false;
55454     },
55455     
55456     /**
55457      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55458      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55459      * @param {Boolean} preservePanel Overrides the config preservePanel option
55460      * @return {Roo.ContentPanel} The panel that was removed
55461      */
55462     remove : function(panel, preservePanel){
55463         panel = this.getPanel(panel);
55464         if(!panel){
55465             return null;
55466         }
55467         var e = {};
55468         this.fireEvent("beforeremove", this, panel, e);
55469         if(e.cancel === true){
55470             return null;
55471         }
55472         var panelId = panel.getId();
55473         this.panels.removeKey(panelId);
55474         return panel;
55475     },
55476     
55477     /**
55478      * Returns the panel specified or null if it's not in this region.
55479      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55480      * @return {Roo.ContentPanel}
55481      */
55482     getPanel : function(id){
55483         if(typeof id == "object"){ // must be panel obj
55484             return id;
55485         }
55486         return this.panels.get(id);
55487     },
55488     
55489     /**
55490      * Returns this regions position (north/south/east/west/center).
55491      * @return {String} 
55492      */
55493     getPosition: function(){
55494         return this.position;    
55495     }
55496 });/*
55497  * Based on:
55498  * Ext JS Library 1.1.1
55499  * Copyright(c) 2006-2007, Ext JS, LLC.
55500  *
55501  * Originally Released Under LGPL - original licence link has changed is not relivant.
55502  *
55503  * Fork - LGPL
55504  * <script type="text/javascript">
55505  */
55506  
55507 /**
55508  * @class Roo.LayoutRegion
55509  * @extends Roo.BasicLayoutRegion
55510  * This class represents a region in a layout manager.
55511  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55512  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55513  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55514  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55515  * @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})
55516  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55517  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55518  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55519  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55520  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55521  * @cfg {String}    title           The title for the region (overrides panel titles)
55522  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55523  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55524  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55525  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55526  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55527  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55528  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55529  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55530  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55531  * @cfg {Boolean}   showPin         True to show a pin button
55532  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55533  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55534  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55535  * @cfg {Number}    width           For East/West panels
55536  * @cfg {Number}    height          For North/South panels
55537  * @cfg {Boolean}   split           To show the splitter
55538  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55539  */
55540 Roo.LayoutRegion = function(mgr, config, pos){
55541     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55542     var dh = Roo.DomHelper;
55543     /** This region's container element 
55544     * @type Roo.Element */
55545     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55546     /** This region's title element 
55547     * @type Roo.Element */
55548
55549     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55550         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55551         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55552     ]}, true);
55553     this.titleEl.enableDisplayMode();
55554     /** This region's title text element 
55555     * @type HTMLElement */
55556     this.titleTextEl = this.titleEl.dom.firstChild;
55557     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55558     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55559     this.closeBtn.enableDisplayMode();
55560     this.closeBtn.on("click", this.closeClicked, this);
55561     this.closeBtn.hide();
55562
55563     this.createBody(config);
55564     this.visible = true;
55565     this.collapsed = false;
55566
55567     if(config.hideWhenEmpty){
55568         this.hide();
55569         this.on("paneladded", this.validateVisibility, this);
55570         this.on("panelremoved", this.validateVisibility, this);
55571     }
55572     this.applyConfig(config);
55573 };
55574
55575 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55576
55577     createBody : function(){
55578         /** This region's body element 
55579         * @type Roo.Element */
55580         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55581     },
55582
55583     applyConfig : function(c){
55584         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55585             var dh = Roo.DomHelper;
55586             if(c.titlebar !== false){
55587                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55588                 this.collapseBtn.on("click", this.collapse, this);
55589                 this.collapseBtn.enableDisplayMode();
55590
55591                 if(c.showPin === true || this.showPin){
55592                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55593                     this.stickBtn.enableDisplayMode();
55594                     this.stickBtn.on("click", this.expand, this);
55595                     this.stickBtn.hide();
55596                 }
55597             }
55598             /** This region's collapsed element
55599             * @type Roo.Element */
55600             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55601                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55602             ]}, true);
55603             if(c.floatable !== false){
55604                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55605                this.collapsedEl.on("click", this.collapseClick, this);
55606             }
55607
55608             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55609                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55610                    id: "message", unselectable: "on", style:{"float":"left"}});
55611                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55612              }
55613             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55614             this.expandBtn.on("click", this.expand, this);
55615         }
55616         if(this.collapseBtn){
55617             this.collapseBtn.setVisible(c.collapsible == true);
55618         }
55619         this.cmargins = c.cmargins || this.cmargins ||
55620                          (this.position == "west" || this.position == "east" ?
55621                              {top: 0, left: 2, right:2, bottom: 0} :
55622                              {top: 2, left: 0, right:0, bottom: 2});
55623         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55624         this.bottomTabs = c.tabPosition != "top";
55625         this.autoScroll = c.autoScroll || false;
55626         if(this.autoScroll){
55627             this.bodyEl.setStyle("overflow", "auto");
55628         }else{
55629             this.bodyEl.setStyle("overflow", "hidden");
55630         }
55631         //if(c.titlebar !== false){
55632             if((!c.titlebar && !c.title) || c.titlebar === false){
55633                 this.titleEl.hide();
55634             }else{
55635                 this.titleEl.show();
55636                 if(c.title){
55637                     this.titleTextEl.innerHTML = c.title;
55638                 }
55639             }
55640         //}
55641         this.duration = c.duration || .30;
55642         this.slideDuration = c.slideDuration || .45;
55643         this.config = c;
55644         if(c.collapsed){
55645             this.collapse(true);
55646         }
55647         if(c.hidden){
55648             this.hide();
55649         }
55650     },
55651     /**
55652      * Returns true if this region is currently visible.
55653      * @return {Boolean}
55654      */
55655     isVisible : function(){
55656         return this.visible;
55657     },
55658
55659     /**
55660      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55661      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55662      */
55663     setCollapsedTitle : function(title){
55664         title = title || "&#160;";
55665         if(this.collapsedTitleTextEl){
55666             this.collapsedTitleTextEl.innerHTML = title;
55667         }
55668     },
55669
55670     getBox : function(){
55671         var b;
55672         if(!this.collapsed){
55673             b = this.el.getBox(false, true);
55674         }else{
55675             b = this.collapsedEl.getBox(false, true);
55676         }
55677         return b;
55678     },
55679
55680     getMargins : function(){
55681         return this.collapsed ? this.cmargins : this.margins;
55682     },
55683
55684     highlight : function(){
55685         this.el.addClass("x-layout-panel-dragover");
55686     },
55687
55688     unhighlight : function(){
55689         this.el.removeClass("x-layout-panel-dragover");
55690     },
55691
55692     updateBox : function(box){
55693         this.box = box;
55694         if(!this.collapsed){
55695             this.el.dom.style.left = box.x + "px";
55696             this.el.dom.style.top = box.y + "px";
55697             this.updateBody(box.width, box.height);
55698         }else{
55699             this.collapsedEl.dom.style.left = box.x + "px";
55700             this.collapsedEl.dom.style.top = box.y + "px";
55701             this.collapsedEl.setSize(box.width, box.height);
55702         }
55703         if(this.tabs){
55704             this.tabs.autoSizeTabs();
55705         }
55706     },
55707
55708     updateBody : function(w, h){
55709         if(w !== null){
55710             this.el.setWidth(w);
55711             w -= this.el.getBorderWidth("rl");
55712             if(this.config.adjustments){
55713                 w += this.config.adjustments[0];
55714             }
55715         }
55716         if(h !== null){
55717             this.el.setHeight(h);
55718             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55719             h -= this.el.getBorderWidth("tb");
55720             if(this.config.adjustments){
55721                 h += this.config.adjustments[1];
55722             }
55723             this.bodyEl.setHeight(h);
55724             if(this.tabs){
55725                 h = this.tabs.syncHeight(h);
55726             }
55727         }
55728         if(this.panelSize){
55729             w = w !== null ? w : this.panelSize.width;
55730             h = h !== null ? h : this.panelSize.height;
55731         }
55732         if(this.activePanel){
55733             var el = this.activePanel.getEl();
55734             w = w !== null ? w : el.getWidth();
55735             h = h !== null ? h : el.getHeight();
55736             this.panelSize = {width: w, height: h};
55737             this.activePanel.setSize(w, h);
55738         }
55739         if(Roo.isIE && this.tabs){
55740             this.tabs.el.repaint();
55741         }
55742     },
55743
55744     /**
55745      * Returns the container element for this region.
55746      * @return {Roo.Element}
55747      */
55748     getEl : function(){
55749         return this.el;
55750     },
55751
55752     /**
55753      * Hides this region.
55754      */
55755     hide : function(){
55756         if(!this.collapsed){
55757             this.el.dom.style.left = "-2000px";
55758             this.el.hide();
55759         }else{
55760             this.collapsedEl.dom.style.left = "-2000px";
55761             this.collapsedEl.hide();
55762         }
55763         this.visible = false;
55764         this.fireEvent("visibilitychange", this, false);
55765     },
55766
55767     /**
55768      * Shows this region if it was previously hidden.
55769      */
55770     show : function(){
55771         if(!this.collapsed){
55772             this.el.show();
55773         }else{
55774             this.collapsedEl.show();
55775         }
55776         this.visible = true;
55777         this.fireEvent("visibilitychange", this, true);
55778     },
55779
55780     closeClicked : function(){
55781         if(this.activePanel){
55782             this.remove(this.activePanel);
55783         }
55784     },
55785
55786     collapseClick : function(e){
55787         if(this.isSlid){
55788            e.stopPropagation();
55789            this.slideIn();
55790         }else{
55791            e.stopPropagation();
55792            this.slideOut();
55793         }
55794     },
55795
55796     /**
55797      * Collapses this region.
55798      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55799      */
55800     collapse : function(skipAnim, skipCheck){
55801         if(this.collapsed) {
55802             return;
55803         }
55804         
55805         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55806             
55807             this.collapsed = true;
55808             if(this.split){
55809                 this.split.el.hide();
55810             }
55811             if(this.config.animate && skipAnim !== true){
55812                 this.fireEvent("invalidated", this);
55813                 this.animateCollapse();
55814             }else{
55815                 this.el.setLocation(-20000,-20000);
55816                 this.el.hide();
55817                 this.collapsedEl.show();
55818                 this.fireEvent("collapsed", this);
55819                 this.fireEvent("invalidated", this);
55820             }
55821         }
55822         
55823     },
55824
55825     animateCollapse : function(){
55826         // overridden
55827     },
55828
55829     /**
55830      * Expands this region if it was previously collapsed.
55831      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55832      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55833      */
55834     expand : function(e, skipAnim){
55835         if(e) {
55836             e.stopPropagation();
55837         }
55838         if(!this.collapsed || this.el.hasActiveFx()) {
55839             return;
55840         }
55841         if(this.isSlid){
55842             this.afterSlideIn();
55843             skipAnim = true;
55844         }
55845         this.collapsed = false;
55846         if(this.config.animate && skipAnim !== true){
55847             this.animateExpand();
55848         }else{
55849             this.el.show();
55850             if(this.split){
55851                 this.split.el.show();
55852             }
55853             this.collapsedEl.setLocation(-2000,-2000);
55854             this.collapsedEl.hide();
55855             this.fireEvent("invalidated", this);
55856             this.fireEvent("expanded", this);
55857         }
55858     },
55859
55860     animateExpand : function(){
55861         // overridden
55862     },
55863
55864     initTabs : function()
55865     {
55866         this.bodyEl.setStyle("overflow", "hidden");
55867         var ts = new Roo.TabPanel(
55868                 this.bodyEl.dom,
55869                 {
55870                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55871                     disableTooltips: this.config.disableTabTips,
55872                     toolbar : this.config.toolbar
55873                 }
55874         );
55875         if(this.config.hideTabs){
55876             ts.stripWrap.setDisplayed(false);
55877         }
55878         this.tabs = ts;
55879         ts.resizeTabs = this.config.resizeTabs === true;
55880         ts.minTabWidth = this.config.minTabWidth || 40;
55881         ts.maxTabWidth = this.config.maxTabWidth || 250;
55882         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55883         ts.monitorResize = false;
55884         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55885         ts.bodyEl.addClass('x-layout-tabs-body');
55886         this.panels.each(this.initPanelAsTab, this);
55887     },
55888
55889     initPanelAsTab : function(panel){
55890         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55891                     this.config.closeOnTab && panel.isClosable());
55892         if(panel.tabTip !== undefined){
55893             ti.setTooltip(panel.tabTip);
55894         }
55895         ti.on("activate", function(){
55896               this.setActivePanel(panel);
55897         }, this);
55898         if(this.config.closeOnTab){
55899             ti.on("beforeclose", function(t, e){
55900                 e.cancel = true;
55901                 this.remove(panel);
55902             }, this);
55903         }
55904         return ti;
55905     },
55906
55907     updatePanelTitle : function(panel, title){
55908         if(this.activePanel == panel){
55909             this.updateTitle(title);
55910         }
55911         if(this.tabs){
55912             var ti = this.tabs.getTab(panel.getEl().id);
55913             ti.setText(title);
55914             if(panel.tabTip !== undefined){
55915                 ti.setTooltip(panel.tabTip);
55916             }
55917         }
55918     },
55919
55920     updateTitle : function(title){
55921         if(this.titleTextEl && !this.config.title){
55922             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55923         }
55924     },
55925
55926     setActivePanel : function(panel){
55927         panel = this.getPanel(panel);
55928         if(this.activePanel && this.activePanel != panel){
55929             this.activePanel.setActiveState(false);
55930         }
55931         this.activePanel = panel;
55932         panel.setActiveState(true);
55933         if(this.panelSize){
55934             panel.setSize(this.panelSize.width, this.panelSize.height);
55935         }
55936         if(this.closeBtn){
55937             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55938         }
55939         this.updateTitle(panel.getTitle());
55940         if(this.tabs){
55941             this.fireEvent("invalidated", this);
55942         }
55943         this.fireEvent("panelactivated", this, panel);
55944     },
55945
55946     /**
55947      * Shows the specified panel.
55948      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55949      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55950      */
55951     showPanel : function(panel)
55952     {
55953         panel = this.getPanel(panel);
55954         if(panel){
55955             if(this.tabs){
55956                 var tab = this.tabs.getTab(panel.getEl().id);
55957                 if(tab.isHidden()){
55958                     this.tabs.unhideTab(tab.id);
55959                 }
55960                 tab.activate();
55961             }else{
55962                 this.setActivePanel(panel);
55963             }
55964         }
55965         return panel;
55966     },
55967
55968     /**
55969      * Get the active panel for this region.
55970      * @return {Roo.ContentPanel} The active panel or null
55971      */
55972     getActivePanel : function(){
55973         return this.activePanel;
55974     },
55975
55976     validateVisibility : function(){
55977         if(this.panels.getCount() < 1){
55978             this.updateTitle("&#160;");
55979             this.closeBtn.hide();
55980             this.hide();
55981         }else{
55982             if(!this.isVisible()){
55983                 this.show();
55984             }
55985         }
55986     },
55987
55988     /**
55989      * Adds the passed ContentPanel(s) to this region.
55990      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55991      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55992      */
55993     add : function(panel){
55994         if(arguments.length > 1){
55995             for(var i = 0, len = arguments.length; i < len; i++) {
55996                 this.add(arguments[i]);
55997             }
55998             return null;
55999         }
56000         if(this.hasPanel(panel)){
56001             this.showPanel(panel);
56002             return panel;
56003         }
56004         panel.setRegion(this);
56005         this.panels.add(panel);
56006         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56007             this.bodyEl.dom.appendChild(panel.getEl().dom);
56008             if(panel.background !== true){
56009                 this.setActivePanel(panel);
56010             }
56011             this.fireEvent("paneladded", this, panel);
56012             return panel;
56013         }
56014         if(!this.tabs){
56015             this.initTabs();
56016         }else{
56017             this.initPanelAsTab(panel);
56018         }
56019         if(panel.background !== true){
56020             this.tabs.activate(panel.getEl().id);
56021         }
56022         this.fireEvent("paneladded", this, panel);
56023         return panel;
56024     },
56025
56026     /**
56027      * Hides the tab for the specified panel.
56028      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56029      */
56030     hidePanel : function(panel){
56031         if(this.tabs && (panel = this.getPanel(panel))){
56032             this.tabs.hideTab(panel.getEl().id);
56033         }
56034     },
56035
56036     /**
56037      * Unhides the tab for a previously hidden panel.
56038      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56039      */
56040     unhidePanel : function(panel){
56041         if(this.tabs && (panel = this.getPanel(panel))){
56042             this.tabs.unhideTab(panel.getEl().id);
56043         }
56044     },
56045
56046     clearPanels : function(){
56047         while(this.panels.getCount() > 0){
56048              this.remove(this.panels.first());
56049         }
56050     },
56051
56052     /**
56053      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56054      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56055      * @param {Boolean} preservePanel Overrides the config preservePanel option
56056      * @return {Roo.ContentPanel} The panel that was removed
56057      */
56058     remove : function(panel, preservePanel){
56059         panel = this.getPanel(panel);
56060         if(!panel){
56061             return null;
56062         }
56063         var e = {};
56064         this.fireEvent("beforeremove", this, panel, e);
56065         if(e.cancel === true){
56066             return null;
56067         }
56068         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56069         var panelId = panel.getId();
56070         this.panels.removeKey(panelId);
56071         if(preservePanel){
56072             document.body.appendChild(panel.getEl().dom);
56073         }
56074         if(this.tabs){
56075             this.tabs.removeTab(panel.getEl().id);
56076         }else if (!preservePanel){
56077             this.bodyEl.dom.removeChild(panel.getEl().dom);
56078         }
56079         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56080             var p = this.panels.first();
56081             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56082             tempEl.appendChild(p.getEl().dom);
56083             this.bodyEl.update("");
56084             this.bodyEl.dom.appendChild(p.getEl().dom);
56085             tempEl = null;
56086             this.updateTitle(p.getTitle());
56087             this.tabs = null;
56088             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56089             this.setActivePanel(p);
56090         }
56091         panel.setRegion(null);
56092         if(this.activePanel == panel){
56093             this.activePanel = null;
56094         }
56095         if(this.config.autoDestroy !== false && preservePanel !== true){
56096             try{panel.destroy();}catch(e){}
56097         }
56098         this.fireEvent("panelremoved", this, panel);
56099         return panel;
56100     },
56101
56102     /**
56103      * Returns the TabPanel component used by this region
56104      * @return {Roo.TabPanel}
56105      */
56106     getTabs : function(){
56107         return this.tabs;
56108     },
56109
56110     createTool : function(parentEl, className){
56111         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56112             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56113         btn.addClassOnOver("x-layout-tools-button-over");
56114         return btn;
56115     }
56116 });/*
56117  * Based on:
56118  * Ext JS Library 1.1.1
56119  * Copyright(c) 2006-2007, Ext JS, LLC.
56120  *
56121  * Originally Released Under LGPL - original licence link has changed is not relivant.
56122  *
56123  * Fork - LGPL
56124  * <script type="text/javascript">
56125  */
56126  
56127
56128
56129 /**
56130  * @class Roo.SplitLayoutRegion
56131  * @extends Roo.LayoutRegion
56132  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56133  */
56134 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56135     this.cursor = cursor;
56136     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56137 };
56138
56139 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56140     splitTip : "Drag to resize.",
56141     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56142     useSplitTips : false,
56143
56144     applyConfig : function(config){
56145         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56146         if(config.split){
56147             if(!this.split){
56148                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56149                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56150                 /** The SplitBar for this region 
56151                 * @type Roo.SplitBar */
56152                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56153                 this.split.on("moved", this.onSplitMove, this);
56154                 this.split.useShim = config.useShim === true;
56155                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56156                 if(this.useSplitTips){
56157                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56158                 }
56159                 if(config.collapsible){
56160                     this.split.el.on("dblclick", this.collapse,  this);
56161                 }
56162             }
56163             if(typeof config.minSize != "undefined"){
56164                 this.split.minSize = config.minSize;
56165             }
56166             if(typeof config.maxSize != "undefined"){
56167                 this.split.maxSize = config.maxSize;
56168             }
56169             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56170                 this.hideSplitter();
56171             }
56172         }
56173     },
56174
56175     getHMaxSize : function(){
56176          var cmax = this.config.maxSize || 10000;
56177          var center = this.mgr.getRegion("center");
56178          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56179     },
56180
56181     getVMaxSize : function(){
56182          var cmax = this.config.maxSize || 10000;
56183          var center = this.mgr.getRegion("center");
56184          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56185     },
56186
56187     onSplitMove : function(split, newSize){
56188         this.fireEvent("resized", this, newSize);
56189     },
56190     
56191     /** 
56192      * Returns the {@link Roo.SplitBar} for this region.
56193      * @return {Roo.SplitBar}
56194      */
56195     getSplitBar : function(){
56196         return this.split;
56197     },
56198     
56199     hide : function(){
56200         this.hideSplitter();
56201         Roo.SplitLayoutRegion.superclass.hide.call(this);
56202     },
56203
56204     hideSplitter : function(){
56205         if(this.split){
56206             this.split.el.setLocation(-2000,-2000);
56207             this.split.el.hide();
56208         }
56209     },
56210
56211     show : function(){
56212         if(this.split){
56213             this.split.el.show();
56214         }
56215         Roo.SplitLayoutRegion.superclass.show.call(this);
56216     },
56217     
56218     beforeSlide: function(){
56219         if(Roo.isGecko){// firefox overflow auto bug workaround
56220             this.bodyEl.clip();
56221             if(this.tabs) {
56222                 this.tabs.bodyEl.clip();
56223             }
56224             if(this.activePanel){
56225                 this.activePanel.getEl().clip();
56226                 
56227                 if(this.activePanel.beforeSlide){
56228                     this.activePanel.beforeSlide();
56229                 }
56230             }
56231         }
56232     },
56233     
56234     afterSlide : function(){
56235         if(Roo.isGecko){// firefox overflow auto bug workaround
56236             this.bodyEl.unclip();
56237             if(this.tabs) {
56238                 this.tabs.bodyEl.unclip();
56239             }
56240             if(this.activePanel){
56241                 this.activePanel.getEl().unclip();
56242                 if(this.activePanel.afterSlide){
56243                     this.activePanel.afterSlide();
56244                 }
56245             }
56246         }
56247     },
56248
56249     initAutoHide : function(){
56250         if(this.autoHide !== false){
56251             if(!this.autoHideHd){
56252                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56253                 this.autoHideHd = {
56254                     "mouseout": function(e){
56255                         if(!e.within(this.el, true)){
56256                             st.delay(500);
56257                         }
56258                     },
56259                     "mouseover" : function(e){
56260                         st.cancel();
56261                     },
56262                     scope : this
56263                 };
56264             }
56265             this.el.on(this.autoHideHd);
56266         }
56267     },
56268
56269     clearAutoHide : function(){
56270         if(this.autoHide !== false){
56271             this.el.un("mouseout", this.autoHideHd.mouseout);
56272             this.el.un("mouseover", this.autoHideHd.mouseover);
56273         }
56274     },
56275
56276     clearMonitor : function(){
56277         Roo.get(document).un("click", this.slideInIf, this);
56278     },
56279
56280     // these names are backwards but not changed for compat
56281     slideOut : function(){
56282         if(this.isSlid || this.el.hasActiveFx()){
56283             return;
56284         }
56285         this.isSlid = true;
56286         if(this.collapseBtn){
56287             this.collapseBtn.hide();
56288         }
56289         this.closeBtnState = this.closeBtn.getStyle('display');
56290         this.closeBtn.hide();
56291         if(this.stickBtn){
56292             this.stickBtn.show();
56293         }
56294         this.el.show();
56295         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56296         this.beforeSlide();
56297         this.el.setStyle("z-index", 10001);
56298         this.el.slideIn(this.getSlideAnchor(), {
56299             callback: function(){
56300                 this.afterSlide();
56301                 this.initAutoHide();
56302                 Roo.get(document).on("click", this.slideInIf, this);
56303                 this.fireEvent("slideshow", this);
56304             },
56305             scope: this,
56306             block: true
56307         });
56308     },
56309
56310     afterSlideIn : function(){
56311         this.clearAutoHide();
56312         this.isSlid = false;
56313         this.clearMonitor();
56314         this.el.setStyle("z-index", "");
56315         if(this.collapseBtn){
56316             this.collapseBtn.show();
56317         }
56318         this.closeBtn.setStyle('display', this.closeBtnState);
56319         if(this.stickBtn){
56320             this.stickBtn.hide();
56321         }
56322         this.fireEvent("slidehide", this);
56323     },
56324
56325     slideIn : function(cb){
56326         if(!this.isSlid || this.el.hasActiveFx()){
56327             Roo.callback(cb);
56328             return;
56329         }
56330         this.isSlid = false;
56331         this.beforeSlide();
56332         this.el.slideOut(this.getSlideAnchor(), {
56333             callback: function(){
56334                 this.el.setLeftTop(-10000, -10000);
56335                 this.afterSlide();
56336                 this.afterSlideIn();
56337                 Roo.callback(cb);
56338             },
56339             scope: this,
56340             block: true
56341         });
56342     },
56343     
56344     slideInIf : function(e){
56345         if(!e.within(this.el)){
56346             this.slideIn();
56347         }
56348     },
56349
56350     animateCollapse : function(){
56351         this.beforeSlide();
56352         this.el.setStyle("z-index", 20000);
56353         var anchor = this.getSlideAnchor();
56354         this.el.slideOut(anchor, {
56355             callback : function(){
56356                 this.el.setStyle("z-index", "");
56357                 this.collapsedEl.slideIn(anchor, {duration:.3});
56358                 this.afterSlide();
56359                 this.el.setLocation(-10000,-10000);
56360                 this.el.hide();
56361                 this.fireEvent("collapsed", this);
56362             },
56363             scope: this,
56364             block: true
56365         });
56366     },
56367
56368     animateExpand : function(){
56369         this.beforeSlide();
56370         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56371         this.el.setStyle("z-index", 20000);
56372         this.collapsedEl.hide({
56373             duration:.1
56374         });
56375         this.el.slideIn(this.getSlideAnchor(), {
56376             callback : function(){
56377                 this.el.setStyle("z-index", "");
56378                 this.afterSlide();
56379                 if(this.split){
56380                     this.split.el.show();
56381                 }
56382                 this.fireEvent("invalidated", this);
56383                 this.fireEvent("expanded", this);
56384             },
56385             scope: this,
56386             block: true
56387         });
56388     },
56389
56390     anchors : {
56391         "west" : "left",
56392         "east" : "right",
56393         "north" : "top",
56394         "south" : "bottom"
56395     },
56396
56397     sanchors : {
56398         "west" : "l",
56399         "east" : "r",
56400         "north" : "t",
56401         "south" : "b"
56402     },
56403
56404     canchors : {
56405         "west" : "tl-tr",
56406         "east" : "tr-tl",
56407         "north" : "tl-bl",
56408         "south" : "bl-tl"
56409     },
56410
56411     getAnchor : function(){
56412         return this.anchors[this.position];
56413     },
56414
56415     getCollapseAnchor : function(){
56416         return this.canchors[this.position];
56417     },
56418
56419     getSlideAnchor : function(){
56420         return this.sanchors[this.position];
56421     },
56422
56423     getAlignAdj : function(){
56424         var cm = this.cmargins;
56425         switch(this.position){
56426             case "west":
56427                 return [0, 0];
56428             break;
56429             case "east":
56430                 return [0, 0];
56431             break;
56432             case "north":
56433                 return [0, 0];
56434             break;
56435             case "south":
56436                 return [0, 0];
56437             break;
56438         }
56439     },
56440
56441     getExpandAdj : function(){
56442         var c = this.collapsedEl, cm = this.cmargins;
56443         switch(this.position){
56444             case "west":
56445                 return [-(cm.right+c.getWidth()+cm.left), 0];
56446             break;
56447             case "east":
56448                 return [cm.right+c.getWidth()+cm.left, 0];
56449             break;
56450             case "north":
56451                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56452             break;
56453             case "south":
56454                 return [0, cm.top+cm.bottom+c.getHeight()];
56455             break;
56456         }
56457     }
56458 });/*
56459  * Based on:
56460  * Ext JS Library 1.1.1
56461  * Copyright(c) 2006-2007, Ext JS, LLC.
56462  *
56463  * Originally Released Under LGPL - original licence link has changed is not relivant.
56464  *
56465  * Fork - LGPL
56466  * <script type="text/javascript">
56467  */
56468 /*
56469  * These classes are private internal classes
56470  */
56471 Roo.CenterLayoutRegion = function(mgr, config){
56472     Roo.LayoutRegion.call(this, mgr, config, "center");
56473     this.visible = true;
56474     this.minWidth = config.minWidth || 20;
56475     this.minHeight = config.minHeight || 20;
56476 };
56477
56478 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56479     hide : function(){
56480         // center panel can't be hidden
56481     },
56482     
56483     show : function(){
56484         // center panel can't be hidden
56485     },
56486     
56487     getMinWidth: function(){
56488         return this.minWidth;
56489     },
56490     
56491     getMinHeight: function(){
56492         return this.minHeight;
56493     }
56494 });
56495
56496
56497 Roo.NorthLayoutRegion = function(mgr, config){
56498     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56499     if(this.split){
56500         this.split.placement = Roo.SplitBar.TOP;
56501         this.split.orientation = Roo.SplitBar.VERTICAL;
56502         this.split.el.addClass("x-layout-split-v");
56503     }
56504     var size = config.initialSize || config.height;
56505     if(typeof size != "undefined"){
56506         this.el.setHeight(size);
56507     }
56508 };
56509 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56510     orientation: Roo.SplitBar.VERTICAL,
56511     getBox : function(){
56512         if(this.collapsed){
56513             return this.collapsedEl.getBox();
56514         }
56515         var box = this.el.getBox();
56516         if(this.split){
56517             box.height += this.split.el.getHeight();
56518         }
56519         return box;
56520     },
56521     
56522     updateBox : function(box){
56523         if(this.split && !this.collapsed){
56524             box.height -= this.split.el.getHeight();
56525             this.split.el.setLeft(box.x);
56526             this.split.el.setTop(box.y+box.height);
56527             this.split.el.setWidth(box.width);
56528         }
56529         if(this.collapsed){
56530             this.updateBody(box.width, null);
56531         }
56532         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56533     }
56534 });
56535
56536 Roo.SouthLayoutRegion = function(mgr, config){
56537     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56538     if(this.split){
56539         this.split.placement = Roo.SplitBar.BOTTOM;
56540         this.split.orientation = Roo.SplitBar.VERTICAL;
56541         this.split.el.addClass("x-layout-split-v");
56542     }
56543     var size = config.initialSize || config.height;
56544     if(typeof size != "undefined"){
56545         this.el.setHeight(size);
56546     }
56547 };
56548 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56549     orientation: Roo.SplitBar.VERTICAL,
56550     getBox : function(){
56551         if(this.collapsed){
56552             return this.collapsedEl.getBox();
56553         }
56554         var box = this.el.getBox();
56555         if(this.split){
56556             var sh = this.split.el.getHeight();
56557             box.height += sh;
56558             box.y -= sh;
56559         }
56560         return box;
56561     },
56562     
56563     updateBox : function(box){
56564         if(this.split && !this.collapsed){
56565             var sh = this.split.el.getHeight();
56566             box.height -= sh;
56567             box.y += sh;
56568             this.split.el.setLeft(box.x);
56569             this.split.el.setTop(box.y-sh);
56570             this.split.el.setWidth(box.width);
56571         }
56572         if(this.collapsed){
56573             this.updateBody(box.width, null);
56574         }
56575         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56576     }
56577 });
56578
56579 Roo.EastLayoutRegion = function(mgr, config){
56580     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56581     if(this.split){
56582         this.split.placement = Roo.SplitBar.RIGHT;
56583         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56584         this.split.el.addClass("x-layout-split-h");
56585     }
56586     var size = config.initialSize || config.width;
56587     if(typeof size != "undefined"){
56588         this.el.setWidth(size);
56589     }
56590 };
56591 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56592     orientation: Roo.SplitBar.HORIZONTAL,
56593     getBox : function(){
56594         if(this.collapsed){
56595             return this.collapsedEl.getBox();
56596         }
56597         var box = this.el.getBox();
56598         if(this.split){
56599             var sw = this.split.el.getWidth();
56600             box.width += sw;
56601             box.x -= sw;
56602         }
56603         return box;
56604     },
56605
56606     updateBox : function(box){
56607         if(this.split && !this.collapsed){
56608             var sw = this.split.el.getWidth();
56609             box.width -= sw;
56610             this.split.el.setLeft(box.x);
56611             this.split.el.setTop(box.y);
56612             this.split.el.setHeight(box.height);
56613             box.x += sw;
56614         }
56615         if(this.collapsed){
56616             this.updateBody(null, box.height);
56617         }
56618         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56619     }
56620 });
56621
56622 Roo.WestLayoutRegion = function(mgr, config){
56623     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56624     if(this.split){
56625         this.split.placement = Roo.SplitBar.LEFT;
56626         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56627         this.split.el.addClass("x-layout-split-h");
56628     }
56629     var size = config.initialSize || config.width;
56630     if(typeof size != "undefined"){
56631         this.el.setWidth(size);
56632     }
56633 };
56634 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56635     orientation: Roo.SplitBar.HORIZONTAL,
56636     getBox : function(){
56637         if(this.collapsed){
56638             return this.collapsedEl.getBox();
56639         }
56640         var box = this.el.getBox();
56641         if(this.split){
56642             box.width += this.split.el.getWidth();
56643         }
56644         return box;
56645     },
56646     
56647     updateBox : function(box){
56648         if(this.split && !this.collapsed){
56649             var sw = this.split.el.getWidth();
56650             box.width -= sw;
56651             this.split.el.setLeft(box.x+box.width);
56652             this.split.el.setTop(box.y);
56653             this.split.el.setHeight(box.height);
56654         }
56655         if(this.collapsed){
56656             this.updateBody(null, box.height);
56657         }
56658         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56659     }
56660 });
56661 /*
56662  * Based on:
56663  * Ext JS Library 1.1.1
56664  * Copyright(c) 2006-2007, Ext JS, LLC.
56665  *
56666  * Originally Released Under LGPL - original licence link has changed is not relivant.
56667  *
56668  * Fork - LGPL
56669  * <script type="text/javascript">
56670  */
56671  
56672  
56673 /*
56674  * Private internal class for reading and applying state
56675  */
56676 Roo.LayoutStateManager = function(layout){
56677      // default empty state
56678      this.state = {
56679         north: {},
56680         south: {},
56681         east: {},
56682         west: {}       
56683     };
56684 };
56685
56686 Roo.LayoutStateManager.prototype = {
56687     init : function(layout, provider){
56688         this.provider = provider;
56689         var state = provider.get(layout.id+"-layout-state");
56690         if(state){
56691             var wasUpdating = layout.isUpdating();
56692             if(!wasUpdating){
56693                 layout.beginUpdate();
56694             }
56695             for(var key in state){
56696                 if(typeof state[key] != "function"){
56697                     var rstate = state[key];
56698                     var r = layout.getRegion(key);
56699                     if(r && rstate){
56700                         if(rstate.size){
56701                             r.resizeTo(rstate.size);
56702                         }
56703                         if(rstate.collapsed == true){
56704                             r.collapse(true);
56705                         }else{
56706                             r.expand(null, true);
56707                         }
56708                     }
56709                 }
56710             }
56711             if(!wasUpdating){
56712                 layout.endUpdate();
56713             }
56714             this.state = state; 
56715         }
56716         this.layout = layout;
56717         layout.on("regionresized", this.onRegionResized, this);
56718         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56719         layout.on("regionexpanded", this.onRegionExpanded, this);
56720     },
56721     
56722     storeState : function(){
56723         this.provider.set(this.layout.id+"-layout-state", this.state);
56724     },
56725     
56726     onRegionResized : function(region, newSize){
56727         this.state[region.getPosition()].size = newSize;
56728         this.storeState();
56729     },
56730     
56731     onRegionCollapsed : function(region){
56732         this.state[region.getPosition()].collapsed = true;
56733         this.storeState();
56734     },
56735     
56736     onRegionExpanded : function(region){
56737         this.state[region.getPosition()].collapsed = false;
56738         this.storeState();
56739     }
56740 };/*
56741  * Based on:
56742  * Ext JS Library 1.1.1
56743  * Copyright(c) 2006-2007, Ext JS, LLC.
56744  *
56745  * Originally Released Under LGPL - original licence link has changed is not relivant.
56746  *
56747  * Fork - LGPL
56748  * <script type="text/javascript">
56749  */
56750 /**
56751  * @class Roo.ContentPanel
56752  * @extends Roo.util.Observable
56753  * @children Roo.form.Form Roo.JsonView Roo.View
56754  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
56755  * A basic ContentPanel element.
56756  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56757  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56758  * @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
56759  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56760  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56761  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56762  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56763  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56764  * @cfg {String} title          The title for this panel
56765  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56766  * @cfg {String} url            Calls {@link #setUrl} with this value
56767  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
56768  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56769  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56770  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56771  * @cfg {String}    style  Extra style to add to the content panel
56772  * @cfg {Roo.menu.Menu} menu  popup menu
56773
56774  * @constructor
56775  * Create a new ContentPanel.
56776  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56777  * @param {String/Object} config A string to set only the title or a config object
56778  * @param {String} content (optional) Set the HTML content for this panel
56779  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56780  */
56781 Roo.ContentPanel = function(el, config, content){
56782     
56783      
56784     /*
56785     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56786         config = el;
56787         el = Roo.id();
56788     }
56789     if (config && config.parentLayout) { 
56790         el = config.parentLayout.el.createChild(); 
56791     }
56792     */
56793     if(el.autoCreate){ // xtype is available if this is called from factory
56794         config = el;
56795         el = Roo.id();
56796     }
56797     this.el = Roo.get(el);
56798     if(!this.el && config && config.autoCreate){
56799         if(typeof config.autoCreate == "object"){
56800             if(!config.autoCreate.id){
56801                 config.autoCreate.id = config.id||el;
56802             }
56803             this.el = Roo.DomHelper.append(document.body,
56804                         config.autoCreate, true);
56805         }else{
56806             this.el = Roo.DomHelper.append(document.body,
56807                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56808         }
56809     }
56810     
56811     
56812     this.closable = false;
56813     this.loaded = false;
56814     this.active = false;
56815     if(typeof config == "string"){
56816         this.title = config;
56817     }else{
56818         Roo.apply(this, config);
56819     }
56820     
56821     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56822         this.wrapEl = this.el.wrap();
56823         this.toolbar.container = this.el.insertSibling(false, 'before');
56824         this.toolbar = new Roo.Toolbar(this.toolbar);
56825     }
56826     
56827     // xtype created footer. - not sure if will work as we normally have to render first..
56828     if (this.footer && !this.footer.el && this.footer.xtype) {
56829         if (!this.wrapEl) {
56830             this.wrapEl = this.el.wrap();
56831         }
56832     
56833         this.footer.container = this.wrapEl.createChild();
56834          
56835         this.footer = Roo.factory(this.footer, Roo);
56836         
56837     }
56838     
56839     if(this.resizeEl){
56840         this.resizeEl = Roo.get(this.resizeEl, true);
56841     }else{
56842         this.resizeEl = this.el;
56843     }
56844     // handle view.xtype
56845     
56846  
56847     
56848     
56849     this.addEvents({
56850         /**
56851          * @event activate
56852          * Fires when this panel is activated. 
56853          * @param {Roo.ContentPanel} this
56854          */
56855         "activate" : true,
56856         /**
56857          * @event deactivate
56858          * Fires when this panel is activated. 
56859          * @param {Roo.ContentPanel} this
56860          */
56861         "deactivate" : true,
56862
56863         /**
56864          * @event resize
56865          * Fires when this panel is resized if fitToFrame is true.
56866          * @param {Roo.ContentPanel} this
56867          * @param {Number} width The width after any component adjustments
56868          * @param {Number} height The height after any component adjustments
56869          */
56870         "resize" : true,
56871         
56872          /**
56873          * @event render
56874          * Fires when this tab is created
56875          * @param {Roo.ContentPanel} this
56876          */
56877         "render" : true
56878          
56879         
56880     });
56881     
56882
56883     
56884     
56885     if(this.autoScroll){
56886         this.resizeEl.setStyle("overflow", "auto");
56887     } else {
56888         // fix randome scrolling
56889         this.el.on('scroll', function() {
56890             Roo.log('fix random scolling');
56891             this.scrollTo('top',0); 
56892         });
56893     }
56894     content = content || this.content;
56895     if(content){
56896         this.setContent(content);
56897     }
56898     if(config && config.url){
56899         this.setUrl(this.url, this.params, this.loadOnce);
56900     }
56901     
56902     
56903     
56904     Roo.ContentPanel.superclass.constructor.call(this);
56905     
56906     if (this.view && typeof(this.view.xtype) != 'undefined') {
56907         this.view.el = this.el.appendChild(document.createElement("div"));
56908         this.view = Roo.factory(this.view); 
56909         this.view.render  &&  this.view.render(false, '');  
56910     }
56911     
56912     
56913     this.fireEvent('render', this);
56914 };
56915
56916 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56917     tabTip:'',
56918     setRegion : function(region){
56919         this.region = region;
56920         if(region){
56921            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56922         }else{
56923            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56924         } 
56925     },
56926     
56927     /**
56928      * Returns the toolbar for this Panel if one was configured. 
56929      * @return {Roo.Toolbar} 
56930      */
56931     getToolbar : function(){
56932         return this.toolbar;
56933     },
56934     
56935     setActiveState : function(active){
56936         this.active = active;
56937         if(!active){
56938             this.fireEvent("deactivate", this);
56939         }else{
56940             this.fireEvent("activate", this);
56941         }
56942     },
56943     /**
56944      * Updates this panel's element
56945      * @param {String} content The new content
56946      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56947     */
56948     setContent : function(content, loadScripts){
56949         this.el.update(content, loadScripts);
56950     },
56951
56952     ignoreResize : function(w, h){
56953         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56954             return true;
56955         }else{
56956             this.lastSize = {width: w, height: h};
56957             return false;
56958         }
56959     },
56960     /**
56961      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56962      * @return {Roo.UpdateManager} The UpdateManager
56963      */
56964     getUpdateManager : function(){
56965         return this.el.getUpdateManager();
56966     },
56967      /**
56968      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56969      * @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:
56970 <pre><code>
56971 panel.load({
56972     url: "your-url.php",
56973     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56974     callback: yourFunction,
56975     scope: yourObject, //(optional scope)
56976     discardUrl: false,
56977     nocache: false,
56978     text: "Loading...",
56979     timeout: 30,
56980     scripts: false
56981 });
56982 </code></pre>
56983      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56984      * 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.
56985      * @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}
56986      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56987      * @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.
56988      * @return {Roo.ContentPanel} this
56989      */
56990     load : function(){
56991         var um = this.el.getUpdateManager();
56992         um.update.apply(um, arguments);
56993         return this;
56994     },
56995
56996
56997     /**
56998      * 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.
56999      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57000      * @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)
57001      * @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)
57002      * @return {Roo.UpdateManager} The UpdateManager
57003      */
57004     setUrl : function(url, params, loadOnce){
57005         if(this.refreshDelegate){
57006             this.removeListener("activate", this.refreshDelegate);
57007         }
57008         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57009         this.on("activate", this.refreshDelegate);
57010         return this.el.getUpdateManager();
57011     },
57012     
57013     _handleRefresh : function(url, params, loadOnce){
57014         if(!loadOnce || !this.loaded){
57015             var updater = this.el.getUpdateManager();
57016             updater.update(url, params, this._setLoaded.createDelegate(this));
57017         }
57018     },
57019     
57020     _setLoaded : function(){
57021         this.loaded = true;
57022     }, 
57023     
57024     /**
57025      * Returns this panel's id
57026      * @return {String} 
57027      */
57028     getId : function(){
57029         return this.el.id;
57030     },
57031     
57032     /** 
57033      * Returns this panel's element - used by regiosn to add.
57034      * @return {Roo.Element} 
57035      */
57036     getEl : function(){
57037         return this.wrapEl || this.el;
57038     },
57039     
57040     adjustForComponents : function(width, height)
57041     {
57042         //Roo.log('adjustForComponents ');
57043         if(this.resizeEl != this.el){
57044             width -= this.el.getFrameWidth('lr');
57045             height -= this.el.getFrameWidth('tb');
57046         }
57047         if(this.toolbar){
57048             var te = this.toolbar.getEl();
57049             height -= te.getHeight();
57050             te.setWidth(width);
57051         }
57052         if(this.footer){
57053             var te = this.footer.getEl();
57054             //Roo.log("footer:" + te.getHeight());
57055             
57056             height -= te.getHeight();
57057             te.setWidth(width);
57058         }
57059         
57060         
57061         if(this.adjustments){
57062             width += this.adjustments[0];
57063             height += this.adjustments[1];
57064         }
57065         return {"width": width, "height": height};
57066     },
57067     
57068     setSize : function(width, height){
57069         if(this.fitToFrame && !this.ignoreResize(width, height)){
57070             if(this.fitContainer && this.resizeEl != this.el){
57071                 this.el.setSize(width, height);
57072             }
57073             var size = this.adjustForComponents(width, height);
57074             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57075             this.fireEvent('resize', this, size.width, size.height);
57076         }
57077     },
57078     
57079     /**
57080      * Returns this panel's title
57081      * @return {String} 
57082      */
57083     getTitle : function(){
57084         return this.title;
57085     },
57086     
57087     /**
57088      * Set this panel's title
57089      * @param {String} title
57090      */
57091     setTitle : function(title){
57092         this.title = title;
57093         if(this.region){
57094             this.region.updatePanelTitle(this, title);
57095         }
57096     },
57097     
57098     /**
57099      * Returns true is this panel was configured to be closable
57100      * @return {Boolean} 
57101      */
57102     isClosable : function(){
57103         return this.closable;
57104     },
57105     
57106     beforeSlide : function(){
57107         this.el.clip();
57108         this.resizeEl.clip();
57109     },
57110     
57111     afterSlide : function(){
57112         this.el.unclip();
57113         this.resizeEl.unclip();
57114     },
57115     
57116     /**
57117      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57118      *   Will fail silently if the {@link #setUrl} method has not been called.
57119      *   This does not activate the panel, just updates its content.
57120      */
57121     refresh : function(){
57122         if(this.refreshDelegate){
57123            this.loaded = false;
57124            this.refreshDelegate();
57125         }
57126     },
57127     
57128     /**
57129      * Destroys this panel
57130      */
57131     destroy : function(){
57132         this.el.removeAllListeners();
57133         var tempEl = document.createElement("span");
57134         tempEl.appendChild(this.el.dom);
57135         tempEl.innerHTML = "";
57136         this.el.remove();
57137         this.el = null;
57138     },
57139     
57140     /**
57141      * form - if the content panel contains a form - this is a reference to it.
57142      * @type {Roo.form.Form}
57143      */
57144     form : false,
57145     /**
57146      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57147      *    This contains a reference to it.
57148      * @type {Roo.View}
57149      */
57150     view : false,
57151     
57152       /**
57153      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57154      * <pre><code>
57155
57156 layout.addxtype({
57157        xtype : 'Form',
57158        items: [ .... ]
57159    }
57160 );
57161
57162 </code></pre>
57163      * @param {Object} cfg Xtype definition of item to add.
57164      */
57165     
57166     addxtype : function(cfg) {
57167         // add form..
57168         if (cfg.xtype.match(/^Form$/)) {
57169             
57170             var el;
57171             //if (this.footer) {
57172             //    el = this.footer.container.insertSibling(false, 'before');
57173             //} else {
57174                 el = this.el.createChild();
57175             //}
57176
57177             this.form = new  Roo.form.Form(cfg);
57178             
57179             
57180             if ( this.form.allItems.length) {
57181                 this.form.render(el.dom);
57182             }
57183             return this.form;
57184         }
57185         // should only have one of theses..
57186         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57187             // views.. should not be just added - used named prop 'view''
57188             
57189             cfg.el = this.el.appendChild(document.createElement("div"));
57190             // factory?
57191             
57192             var ret = new Roo.factory(cfg);
57193              
57194              ret.render && ret.render(false, ''); // render blank..
57195             this.view = ret;
57196             return ret;
57197         }
57198         return false;
57199     }
57200 });
57201
57202 /**
57203  * @class Roo.GridPanel
57204  * @extends Roo.ContentPanel
57205  * @constructor
57206  * Create a new GridPanel.
57207  * @param {Roo.grid.Grid} grid The grid for this panel
57208  * @param {String/Object} config A string to set only the panel's title, or a config object
57209  */
57210 Roo.GridPanel = function(grid, config){
57211     
57212   
57213     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57214         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57215         
57216     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57217     
57218     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57219     
57220     if(this.toolbar){
57221         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57222     }
57223     // xtype created footer. - not sure if will work as we normally have to render first..
57224     if (this.footer && !this.footer.el && this.footer.xtype) {
57225         
57226         this.footer.container = this.grid.getView().getFooterPanel(true);
57227         this.footer.dataSource = this.grid.dataSource;
57228         this.footer = Roo.factory(this.footer, Roo);
57229         
57230     }
57231     
57232     grid.monitorWindowResize = false; // turn off autosizing
57233     grid.autoHeight = false;
57234     grid.autoWidth = false;
57235     this.grid = grid;
57236     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57237 };
57238
57239 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57240     getId : function(){
57241         return this.grid.id;
57242     },
57243     
57244     /**
57245      * Returns the grid for this panel
57246      * @return {Roo.grid.Grid} 
57247      */
57248     getGrid : function(){
57249         return this.grid;    
57250     },
57251     
57252     setSize : function(width, height){
57253         if(!this.ignoreResize(width, height)){
57254             var grid = this.grid;
57255             var size = this.adjustForComponents(width, height);
57256             grid.getGridEl().setSize(size.width, size.height);
57257             grid.autoSize();
57258         }
57259     },
57260     
57261     beforeSlide : function(){
57262         this.grid.getView().scroller.clip();
57263     },
57264     
57265     afterSlide : function(){
57266         this.grid.getView().scroller.unclip();
57267     },
57268     
57269     destroy : function(){
57270         this.grid.destroy();
57271         delete this.grid;
57272         Roo.GridPanel.superclass.destroy.call(this); 
57273     }
57274 });
57275
57276
57277 /**
57278  * @class Roo.NestedLayoutPanel
57279  * @extends Roo.ContentPanel
57280  * @constructor
57281  * Create a new NestedLayoutPanel.
57282  * 
57283  * 
57284  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57285  * @param {String/Object} config A string to set only the title or a config object
57286  */
57287 Roo.NestedLayoutPanel = function(layout, config)
57288 {
57289     // construct with only one argument..
57290     /* FIXME - implement nicer consturctors
57291     if (layout.layout) {
57292         config = layout;
57293         layout = config.layout;
57294         delete config.layout;
57295     }
57296     if (layout.xtype && !layout.getEl) {
57297         // then layout needs constructing..
57298         layout = Roo.factory(layout, Roo);
57299     }
57300     */
57301     
57302     
57303     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57304     
57305     layout.monitorWindowResize = false; // turn off autosizing
57306     this.layout = layout;
57307     this.layout.getEl().addClass("x-layout-nested-layout");
57308     
57309     
57310     
57311     
57312 };
57313
57314 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57315
57316     setSize : function(width, height){
57317         if(!this.ignoreResize(width, height)){
57318             var size = this.adjustForComponents(width, height);
57319             var el = this.layout.getEl();
57320             el.setSize(size.width, size.height);
57321             var touch = el.dom.offsetWidth;
57322             this.layout.layout();
57323             // ie requires a double layout on the first pass
57324             if(Roo.isIE && !this.initialized){
57325                 this.initialized = true;
57326                 this.layout.layout();
57327             }
57328         }
57329     },
57330     
57331     // activate all subpanels if not currently active..
57332     
57333     setActiveState : function(active){
57334         this.active = active;
57335         if(!active){
57336             this.fireEvent("deactivate", this);
57337             return;
57338         }
57339         
57340         this.fireEvent("activate", this);
57341         // not sure if this should happen before or after..
57342         if (!this.layout) {
57343             return; // should not happen..
57344         }
57345         var reg = false;
57346         for (var r in this.layout.regions) {
57347             reg = this.layout.getRegion(r);
57348             if (reg.getActivePanel()) {
57349                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57350                 reg.setActivePanel(reg.getActivePanel());
57351                 continue;
57352             }
57353             if (!reg.panels.length) {
57354                 continue;
57355             }
57356             reg.showPanel(reg.getPanel(0));
57357         }
57358         
57359         
57360         
57361         
57362     },
57363     
57364     /**
57365      * Returns the nested BorderLayout for this panel
57366      * @return {Roo.BorderLayout} 
57367      */
57368     getLayout : function(){
57369         return this.layout;
57370     },
57371     
57372      /**
57373      * Adds a xtype elements to the layout of the nested panel
57374      * <pre><code>
57375
57376 panel.addxtype({
57377        xtype : 'ContentPanel',
57378        region: 'west',
57379        items: [ .... ]
57380    }
57381 );
57382
57383 panel.addxtype({
57384         xtype : 'NestedLayoutPanel',
57385         region: 'west',
57386         layout: {
57387            center: { },
57388            west: { }   
57389         },
57390         items : [ ... list of content panels or nested layout panels.. ]
57391    }
57392 );
57393 </code></pre>
57394      * @param {Object} cfg Xtype definition of item to add.
57395      */
57396     addxtype : function(cfg) {
57397         return this.layout.addxtype(cfg);
57398     
57399     }
57400 });
57401
57402 Roo.ScrollPanel = function(el, config, content){
57403     config = config || {};
57404     config.fitToFrame = true;
57405     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57406     
57407     this.el.dom.style.overflow = "hidden";
57408     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57409     this.el.removeClass("x-layout-inactive-content");
57410     this.el.on("mousewheel", this.onWheel, this);
57411
57412     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57413     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57414     up.unselectable(); down.unselectable();
57415     up.on("click", this.scrollUp, this);
57416     down.on("click", this.scrollDown, this);
57417     up.addClassOnOver("x-scroller-btn-over");
57418     down.addClassOnOver("x-scroller-btn-over");
57419     up.addClassOnClick("x-scroller-btn-click");
57420     down.addClassOnClick("x-scroller-btn-click");
57421     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57422
57423     this.resizeEl = this.el;
57424     this.el = wrap; this.up = up; this.down = down;
57425 };
57426
57427 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57428     increment : 100,
57429     wheelIncrement : 5,
57430     scrollUp : function(){
57431         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57432     },
57433
57434     scrollDown : function(){
57435         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57436     },
57437
57438     afterScroll : function(){
57439         var el = this.resizeEl;
57440         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57441         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57442         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57443     },
57444
57445     setSize : function(){
57446         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57447         this.afterScroll();
57448     },
57449
57450     onWheel : function(e){
57451         var d = e.getWheelDelta();
57452         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57453         this.afterScroll();
57454         e.stopEvent();
57455     },
57456
57457     setContent : function(content, loadScripts){
57458         this.resizeEl.update(content, loadScripts);
57459     }
57460
57461 });
57462
57463
57464
57465 /**
57466  * @class Roo.TreePanel
57467  * @extends Roo.ContentPanel
57468  * Treepanel component
57469  * 
57470  * @constructor
57471  * Create a new TreePanel. - defaults to fit/scoll contents.
57472  * @param {String/Object} config A string to set only the panel's title, or a config object
57473  */
57474 Roo.TreePanel = function(config){
57475     var el = config.el;
57476     var tree = config.tree;
57477     delete config.tree; 
57478     delete config.el; // hopefull!
57479     
57480     // wrapper for IE7 strict & safari scroll issue
57481     
57482     var treeEl = el.createChild();
57483     config.resizeEl = treeEl;
57484     
57485     
57486     
57487     Roo.TreePanel.superclass.constructor.call(this, el, config);
57488  
57489  
57490     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57491     //console.log(tree);
57492     this.on('activate', function()
57493     {
57494         if (this.tree.rendered) {
57495             return;
57496         }
57497         //console.log('render tree');
57498         this.tree.render();
57499     });
57500     // this should not be needed.. - it's actually the 'el' that resizes?
57501     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57502     
57503     //this.on('resize',  function (cp, w, h) {
57504     //        this.tree.innerCt.setWidth(w);
57505     //        this.tree.innerCt.setHeight(h);
57506     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57507     //});
57508
57509         
57510     
57511 };
57512
57513 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57514     fitToFrame : true,
57515     autoScroll : true,
57516     /*
57517      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57518      */
57519     tree : false
57520
57521 });
57522
57523
57524
57525
57526
57527
57528
57529
57530
57531
57532
57533 /*
57534  * Based on:
57535  * Ext JS Library 1.1.1
57536  * Copyright(c) 2006-2007, Ext JS, LLC.
57537  *
57538  * Originally Released Under LGPL - original licence link has changed is not relivant.
57539  *
57540  * Fork - LGPL
57541  * <script type="text/javascript">
57542  */
57543  
57544
57545 /**
57546  * @class Roo.ReaderLayout
57547  * @extends Roo.BorderLayout
57548  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57549  * center region containing two nested regions (a top one for a list view and one for item preview below),
57550  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57551  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57552  * expedites the setup of the overall layout and regions for this common application style.
57553  * Example:
57554  <pre><code>
57555 var reader = new Roo.ReaderLayout();
57556 var CP = Roo.ContentPanel;  // shortcut for adding
57557
57558 reader.beginUpdate();
57559 reader.add("north", new CP("north", "North"));
57560 reader.add("west", new CP("west", {title: "West"}));
57561 reader.add("east", new CP("east", {title: "East"}));
57562
57563 reader.regions.listView.add(new CP("listView", "List"));
57564 reader.regions.preview.add(new CP("preview", "Preview"));
57565 reader.endUpdate();
57566 </code></pre>
57567 * @constructor
57568 * Create a new ReaderLayout
57569 * @param {Object} config Configuration options
57570 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57571 * document.body if omitted)
57572 */
57573 Roo.ReaderLayout = function(config, renderTo){
57574     var c = config || {size:{}};
57575     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57576         north: c.north !== false ? Roo.apply({
57577             split:false,
57578             initialSize: 32,
57579             titlebar: false
57580         }, c.north) : false,
57581         west: c.west !== false ? Roo.apply({
57582             split:true,
57583             initialSize: 200,
57584             minSize: 175,
57585             maxSize: 400,
57586             titlebar: true,
57587             collapsible: true,
57588             animate: true,
57589             margins:{left:5,right:0,bottom:5,top:5},
57590             cmargins:{left:5,right:5,bottom:5,top:5}
57591         }, c.west) : false,
57592         east: c.east !== false ? Roo.apply({
57593             split:true,
57594             initialSize: 200,
57595             minSize: 175,
57596             maxSize: 400,
57597             titlebar: true,
57598             collapsible: true,
57599             animate: true,
57600             margins:{left:0,right:5,bottom:5,top:5},
57601             cmargins:{left:5,right:5,bottom:5,top:5}
57602         }, c.east) : false,
57603         center: Roo.apply({
57604             tabPosition: 'top',
57605             autoScroll:false,
57606             closeOnTab: true,
57607             titlebar:false,
57608             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57609         }, c.center)
57610     });
57611
57612     this.el.addClass('x-reader');
57613
57614     this.beginUpdate();
57615
57616     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57617         south: c.preview !== false ? Roo.apply({
57618             split:true,
57619             initialSize: 200,
57620             minSize: 100,
57621             autoScroll:true,
57622             collapsible:true,
57623             titlebar: true,
57624             cmargins:{top:5,left:0, right:0, bottom:0}
57625         }, c.preview) : false,
57626         center: Roo.apply({
57627             autoScroll:false,
57628             titlebar:false,
57629             minHeight:200
57630         }, c.listView)
57631     });
57632     this.add('center', new Roo.NestedLayoutPanel(inner,
57633             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57634
57635     this.endUpdate();
57636
57637     this.regions.preview = inner.getRegion('south');
57638     this.regions.listView = inner.getRegion('center');
57639 };
57640
57641 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57642  * Based on:
57643  * Ext JS Library 1.1.1
57644  * Copyright(c) 2006-2007, Ext JS, LLC.
57645  *
57646  * Originally Released Under LGPL - original licence link has changed is not relivant.
57647  *
57648  * Fork - LGPL
57649  * <script type="text/javascript">
57650  */
57651  
57652 /**
57653  * @class Roo.grid.Grid
57654  * @extends Roo.util.Observable
57655  * This class represents the primary interface of a component based grid control.
57656  * <br><br>Usage:<pre><code>
57657  var grid = new Roo.grid.Grid("my-container-id", {
57658      ds: myDataStore,
57659      cm: myColModel,
57660      selModel: mySelectionModel,
57661      autoSizeColumns: true,
57662      monitorWindowResize: false,
57663      trackMouseOver: true
57664  });
57665  // set any options
57666  grid.render();
57667  * </code></pre>
57668  * <b>Common Problems:</b><br/>
57669  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57670  * element will correct this<br/>
57671  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57672  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57673  * are unpredictable.<br/>
57674  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57675  * grid to calculate dimensions/offsets.<br/>
57676   * @constructor
57677  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57678  * The container MUST have some type of size defined for the grid to fill. The container will be
57679  * automatically set to position relative if it isn't already.
57680  * @param {Object} config A config object that sets properties on this grid.
57681  */
57682 Roo.grid.Grid = function(container, config){
57683         // initialize the container
57684         this.container = Roo.get(container);
57685         this.container.update("");
57686         this.container.setStyle("overflow", "hidden");
57687     this.container.addClass('x-grid-container');
57688
57689     this.id = this.container.id;
57690
57691     Roo.apply(this, config);
57692     // check and correct shorthanded configs
57693     if(this.ds){
57694         this.dataSource = this.ds;
57695         delete this.ds;
57696     }
57697     if(this.cm){
57698         this.colModel = this.cm;
57699         delete this.cm;
57700     }
57701     if(this.sm){
57702         this.selModel = this.sm;
57703         delete this.sm;
57704     }
57705
57706     if (this.selModel) {
57707         this.selModel = Roo.factory(this.selModel, Roo.grid);
57708         this.sm = this.selModel;
57709         this.sm.xmodule = this.xmodule || false;
57710     }
57711     if (typeof(this.colModel.config) == 'undefined') {
57712         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57713         this.cm = this.colModel;
57714         this.cm.xmodule = this.xmodule || false;
57715     }
57716     if (this.dataSource) {
57717         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57718         this.ds = this.dataSource;
57719         this.ds.xmodule = this.xmodule || false;
57720          
57721     }
57722     
57723     
57724     
57725     if(this.width){
57726         this.container.setWidth(this.width);
57727     }
57728
57729     if(this.height){
57730         this.container.setHeight(this.height);
57731     }
57732     /** @private */
57733         this.addEvents({
57734         // raw events
57735         /**
57736          * @event click
57737          * The raw click event for the entire grid.
57738          * @param {Roo.EventObject} e
57739          */
57740         "click" : true,
57741         /**
57742          * @event dblclick
57743          * The raw dblclick event for the entire grid.
57744          * @param {Roo.EventObject} e
57745          */
57746         "dblclick" : true,
57747         /**
57748          * @event contextmenu
57749          * The raw contextmenu event for the entire grid.
57750          * @param {Roo.EventObject} e
57751          */
57752         "contextmenu" : true,
57753         /**
57754          * @event mousedown
57755          * The raw mousedown event for the entire grid.
57756          * @param {Roo.EventObject} e
57757          */
57758         "mousedown" : true,
57759         /**
57760          * @event mouseup
57761          * The raw mouseup event for the entire grid.
57762          * @param {Roo.EventObject} e
57763          */
57764         "mouseup" : true,
57765         /**
57766          * @event mouseover
57767          * The raw mouseover event for the entire grid.
57768          * @param {Roo.EventObject} e
57769          */
57770         "mouseover" : true,
57771         /**
57772          * @event mouseout
57773          * The raw mouseout event for the entire grid.
57774          * @param {Roo.EventObject} e
57775          */
57776         "mouseout" : true,
57777         /**
57778          * @event keypress
57779          * The raw keypress event for the entire grid.
57780          * @param {Roo.EventObject} e
57781          */
57782         "keypress" : true,
57783         /**
57784          * @event keydown
57785          * The raw keydown event for the entire grid.
57786          * @param {Roo.EventObject} e
57787          */
57788         "keydown" : true,
57789
57790         // custom events
57791
57792         /**
57793          * @event cellclick
57794          * Fires when a cell is clicked
57795          * @param {Grid} this
57796          * @param {Number} rowIndex
57797          * @param {Number} columnIndex
57798          * @param {Roo.EventObject} e
57799          */
57800         "cellclick" : true,
57801         /**
57802          * @event celldblclick
57803          * Fires when a cell is double clicked
57804          * @param {Grid} this
57805          * @param {Number} rowIndex
57806          * @param {Number} columnIndex
57807          * @param {Roo.EventObject} e
57808          */
57809         "celldblclick" : true,
57810         /**
57811          * @event rowclick
57812          * Fires when a row is clicked
57813          * @param {Grid} this
57814          * @param {Number} rowIndex
57815          * @param {Roo.EventObject} e
57816          */
57817         "rowclick" : true,
57818         /**
57819          * @event rowdblclick
57820          * Fires when a row is double clicked
57821          * @param {Grid} this
57822          * @param {Number} rowIndex
57823          * @param {Roo.EventObject} e
57824          */
57825         "rowdblclick" : true,
57826         /**
57827          * @event headerclick
57828          * Fires when a header is clicked
57829          * @param {Grid} this
57830          * @param {Number} columnIndex
57831          * @param {Roo.EventObject} e
57832          */
57833         "headerclick" : true,
57834         /**
57835          * @event headerdblclick
57836          * Fires when a header cell is double clicked
57837          * @param {Grid} this
57838          * @param {Number} columnIndex
57839          * @param {Roo.EventObject} e
57840          */
57841         "headerdblclick" : true,
57842         /**
57843          * @event rowcontextmenu
57844          * Fires when a row is right clicked
57845          * @param {Grid} this
57846          * @param {Number} rowIndex
57847          * @param {Roo.EventObject} e
57848          */
57849         "rowcontextmenu" : true,
57850         /**
57851          * @event cellcontextmenu
57852          * Fires when a cell is right clicked
57853          * @param {Grid} this
57854          * @param {Number} rowIndex
57855          * @param {Number} cellIndex
57856          * @param {Roo.EventObject} e
57857          */
57858          "cellcontextmenu" : true,
57859         /**
57860          * @event headercontextmenu
57861          * Fires when a header is right clicked
57862          * @param {Grid} this
57863          * @param {Number} columnIndex
57864          * @param {Roo.EventObject} e
57865          */
57866         "headercontextmenu" : true,
57867         /**
57868          * @event bodyscroll
57869          * Fires when the body element is scrolled
57870          * @param {Number} scrollLeft
57871          * @param {Number} scrollTop
57872          */
57873         "bodyscroll" : true,
57874         /**
57875          * @event columnresize
57876          * Fires when the user resizes a column
57877          * @param {Number} columnIndex
57878          * @param {Number} newSize
57879          */
57880         "columnresize" : true,
57881         /**
57882          * @event columnmove
57883          * Fires when the user moves a column
57884          * @param {Number} oldIndex
57885          * @param {Number} newIndex
57886          */
57887         "columnmove" : true,
57888         /**
57889          * @event startdrag
57890          * Fires when row(s) start being dragged
57891          * @param {Grid} this
57892          * @param {Roo.GridDD} dd The drag drop object
57893          * @param {event} e The raw browser event
57894          */
57895         "startdrag" : true,
57896         /**
57897          * @event enddrag
57898          * Fires when a drag operation is complete
57899          * @param {Grid} this
57900          * @param {Roo.GridDD} dd The drag drop object
57901          * @param {event} e The raw browser event
57902          */
57903         "enddrag" : true,
57904         /**
57905          * @event dragdrop
57906          * Fires when dragged row(s) are dropped on a valid DD target
57907          * @param {Grid} this
57908          * @param {Roo.GridDD} dd The drag drop object
57909          * @param {String} targetId The target drag drop object
57910          * @param {event} e The raw browser event
57911          */
57912         "dragdrop" : true,
57913         /**
57914          * @event dragover
57915          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57916          * @param {Grid} this
57917          * @param {Roo.GridDD} dd The drag drop object
57918          * @param {String} targetId The target drag drop object
57919          * @param {event} e The raw browser event
57920          */
57921         "dragover" : true,
57922         /**
57923          * @event dragenter
57924          *  Fires when the dragged row(s) first cross another DD target while being dragged
57925          * @param {Grid} this
57926          * @param {Roo.GridDD} dd The drag drop object
57927          * @param {String} targetId The target drag drop object
57928          * @param {event} e The raw browser event
57929          */
57930         "dragenter" : true,
57931         /**
57932          * @event dragout
57933          * Fires when the dragged row(s) leave another DD target while being dragged
57934          * @param {Grid} this
57935          * @param {Roo.GridDD} dd The drag drop object
57936          * @param {String} targetId The target drag drop object
57937          * @param {event} e The raw browser event
57938          */
57939         "dragout" : true,
57940         /**
57941          * @event rowclass
57942          * Fires when a row is rendered, so you can change add a style to it.
57943          * @param {GridView} gridview   The grid view
57944          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57945          */
57946         'rowclass' : true,
57947
57948         /**
57949          * @event render
57950          * Fires when the grid is rendered
57951          * @param {Grid} grid
57952          */
57953         'render' : true
57954     });
57955
57956     Roo.grid.Grid.superclass.constructor.call(this);
57957 };
57958 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57959     
57960     /**
57961          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57962          */
57963         /**
57964          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57965          */
57966         /**
57967          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57968          */
57969         /**
57970          * @cfg {Roo.grid.Store} ds The data store for the grid
57971          */
57972         /**
57973          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57974          */
57975         /**
57976      * @cfg {String} ddGroup - drag drop group.
57977      */
57978       /**
57979      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57980      */
57981
57982     /**
57983      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57984      */
57985     minColumnWidth : 25,
57986
57987     /**
57988      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57989      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57990      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57991      */
57992     autoSizeColumns : false,
57993
57994     /**
57995      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
57996      */
57997     autoSizeHeaders : true,
57998
57999     /**
58000      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58001      */
58002     monitorWindowResize : true,
58003
58004     /**
58005      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58006      * rows measured to get a columns size. Default is 0 (all rows).
58007      */
58008     maxRowsToMeasure : 0,
58009
58010     /**
58011      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58012      */
58013     trackMouseOver : true,
58014
58015     /**
58016     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58017     */
58018       /**
58019     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58020     */
58021     
58022     /**
58023     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58024     */
58025     enableDragDrop : false,
58026     
58027     /**
58028     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58029     */
58030     enableColumnMove : true,
58031     
58032     /**
58033     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58034     */
58035     enableColumnHide : true,
58036     
58037     /**
58038     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58039     */
58040     enableRowHeightSync : false,
58041     
58042     /**
58043     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58044     */
58045     stripeRows : true,
58046     
58047     /**
58048     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58049     */
58050     autoHeight : false,
58051
58052     /**
58053      * @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.
58054      */
58055     autoExpandColumn : false,
58056
58057     /**
58058     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58059     * Default is 50.
58060     */
58061     autoExpandMin : 50,
58062
58063     /**
58064     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58065     */
58066     autoExpandMax : 1000,
58067
58068     /**
58069     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58070     */
58071     view : null,
58072
58073     /**
58074     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58075     */
58076     loadMask : false,
58077     /**
58078     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58079     */
58080     dropTarget: false,
58081     
58082    
58083     
58084     // private
58085     rendered : false,
58086
58087     /**
58088     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58089     * of a fixed width. Default is false.
58090     */
58091     /**
58092     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58093     */
58094     
58095     
58096     /**
58097     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58098     * %0 is replaced with the number of selected rows.
58099     */
58100     ddText : "{0} selected row{1}",
58101     
58102     
58103     /**
58104      * Called once after all setup has been completed and the grid is ready to be rendered.
58105      * @return {Roo.grid.Grid} this
58106      */
58107     render : function()
58108     {
58109         var c = this.container;
58110         // try to detect autoHeight/width mode
58111         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58112             this.autoHeight = true;
58113         }
58114         var view = this.getView();
58115         view.init(this);
58116
58117         c.on("click", this.onClick, this);
58118         c.on("dblclick", this.onDblClick, this);
58119         c.on("contextmenu", this.onContextMenu, this);
58120         c.on("keydown", this.onKeyDown, this);
58121         if (Roo.isTouch) {
58122             c.on("touchstart", this.onTouchStart, this);
58123         }
58124
58125         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58126
58127         this.getSelectionModel().init(this);
58128
58129         view.render();
58130
58131         if(this.loadMask){
58132             this.loadMask = new Roo.LoadMask(this.container,
58133                     Roo.apply({store:this.dataSource}, this.loadMask));
58134         }
58135         
58136         
58137         if (this.toolbar && this.toolbar.xtype) {
58138             this.toolbar.container = this.getView().getHeaderPanel(true);
58139             this.toolbar = new Roo.Toolbar(this.toolbar);
58140         }
58141         if (this.footer && this.footer.xtype) {
58142             this.footer.dataSource = this.getDataSource();
58143             this.footer.container = this.getView().getFooterPanel(true);
58144             this.footer = Roo.factory(this.footer, Roo);
58145         }
58146         if (this.dropTarget && this.dropTarget.xtype) {
58147             delete this.dropTarget.xtype;
58148             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58149         }
58150         
58151         
58152         this.rendered = true;
58153         this.fireEvent('render', this);
58154         return this;
58155     },
58156
58157     /**
58158      * Reconfigures the grid to use a different Store and Column Model.
58159      * The View will be bound to the new objects and refreshed.
58160      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58161      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58162      */
58163     reconfigure : function(dataSource, colModel){
58164         if(this.loadMask){
58165             this.loadMask.destroy();
58166             this.loadMask = new Roo.LoadMask(this.container,
58167                     Roo.apply({store:dataSource}, this.loadMask));
58168         }
58169         this.view.bind(dataSource, colModel);
58170         this.dataSource = dataSource;
58171         this.colModel = colModel;
58172         this.view.refresh(true);
58173     },
58174     /**
58175      * addColumns
58176      * Add's a column, default at the end..
58177      
58178      * @param {int} position to add (default end)
58179      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58180      */
58181     addColumns : function(pos, ar)
58182     {
58183         
58184         for (var i =0;i< ar.length;i++) {
58185             var cfg = ar[i];
58186             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58187             this.cm.lookup[cfg.id] = cfg;
58188         }
58189         
58190         
58191         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58192             pos = this.cm.config.length; //this.cm.config.push(cfg);
58193         } 
58194         pos = Math.max(0,pos);
58195         ar.unshift(0);
58196         ar.unshift(pos);
58197         this.cm.config.splice.apply(this.cm.config, ar);
58198         
58199         
58200         
58201         this.view.generateRules(this.cm);
58202         this.view.refresh(true);
58203         
58204     },
58205     
58206     
58207     
58208     
58209     // private
58210     onKeyDown : function(e){
58211         this.fireEvent("keydown", e);
58212     },
58213
58214     /**
58215      * Destroy this grid.
58216      * @param {Boolean} removeEl True to remove the element
58217      */
58218     destroy : function(removeEl, keepListeners){
58219         if(this.loadMask){
58220             this.loadMask.destroy();
58221         }
58222         var c = this.container;
58223         c.removeAllListeners();
58224         this.view.destroy();
58225         this.colModel.purgeListeners();
58226         if(!keepListeners){
58227             this.purgeListeners();
58228         }
58229         c.update("");
58230         if(removeEl === true){
58231             c.remove();
58232         }
58233     },
58234
58235     // private
58236     processEvent : function(name, e){
58237         // does this fire select???
58238         //Roo.log('grid:processEvent '  + name);
58239         
58240         if (name != 'touchstart' ) {
58241             this.fireEvent(name, e);    
58242         }
58243         
58244         var t = e.getTarget();
58245         var v = this.view;
58246         var header = v.findHeaderIndex(t);
58247         if(header !== false){
58248             var ename = name == 'touchstart' ? 'click' : name;
58249              
58250             this.fireEvent("header" + ename, this, header, e);
58251         }else{
58252             var row = v.findRowIndex(t);
58253             var cell = v.findCellIndex(t);
58254             if (name == 'touchstart') {
58255                 // first touch is always a click.
58256                 // hopefull this happens after selection is updated.?
58257                 name = false;
58258                 
58259                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58260                     var cs = this.selModel.getSelectedCell();
58261                     if (row == cs[0] && cell == cs[1]){
58262                         name = 'dblclick';
58263                     }
58264                 }
58265                 if (typeof(this.selModel.getSelections) != 'undefined') {
58266                     var cs = this.selModel.getSelections();
58267                     var ds = this.dataSource;
58268                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58269                         name = 'dblclick';
58270                     }
58271                 }
58272                 if (!name) {
58273                     return;
58274                 }
58275             }
58276             
58277             
58278             if(row !== false){
58279                 this.fireEvent("row" + name, this, row, e);
58280                 if(cell !== false){
58281                     this.fireEvent("cell" + name, this, row, cell, e);
58282                 }
58283             }
58284         }
58285     },
58286
58287     // private
58288     onClick : function(e){
58289         this.processEvent("click", e);
58290     },
58291    // private
58292     onTouchStart : function(e){
58293         this.processEvent("touchstart", e);
58294     },
58295
58296     // private
58297     onContextMenu : function(e, t){
58298         this.processEvent("contextmenu", e);
58299     },
58300
58301     // private
58302     onDblClick : function(e){
58303         this.processEvent("dblclick", e);
58304     },
58305
58306     // private
58307     walkCells : function(row, col, step, fn, scope){
58308         var cm = this.colModel, clen = cm.getColumnCount();
58309         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58310         if(step < 0){
58311             if(col < 0){
58312                 row--;
58313                 first = false;
58314             }
58315             while(row >= 0){
58316                 if(!first){
58317                     col = clen-1;
58318                 }
58319                 first = false;
58320                 while(col >= 0){
58321                     if(fn.call(scope || this, row, col, cm) === true){
58322                         return [row, col];
58323                     }
58324                     col--;
58325                 }
58326                 row--;
58327             }
58328         } else {
58329             if(col >= clen){
58330                 row++;
58331                 first = false;
58332             }
58333             while(row < rlen){
58334                 if(!first){
58335                     col = 0;
58336                 }
58337                 first = false;
58338                 while(col < clen){
58339                     if(fn.call(scope || this, row, col, cm) === true){
58340                         return [row, col];
58341                     }
58342                     col++;
58343                 }
58344                 row++;
58345             }
58346         }
58347         return null;
58348     },
58349
58350     // private
58351     getSelections : function(){
58352         return this.selModel.getSelections();
58353     },
58354
58355     /**
58356      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58357      * but if manual update is required this method will initiate it.
58358      */
58359     autoSize : function(){
58360         if(this.rendered){
58361             this.view.layout();
58362             if(this.view.adjustForScroll){
58363                 this.view.adjustForScroll();
58364             }
58365         }
58366     },
58367
58368     /**
58369      * Returns the grid's underlying element.
58370      * @return {Element} The element
58371      */
58372     getGridEl : function(){
58373         return this.container;
58374     },
58375
58376     // private for compatibility, overridden by editor grid
58377     stopEditing : function(){},
58378
58379     /**
58380      * Returns the grid's SelectionModel.
58381      * @return {SelectionModel}
58382      */
58383     getSelectionModel : function(){
58384         if(!this.selModel){
58385             this.selModel = new Roo.grid.RowSelectionModel();
58386         }
58387         return this.selModel;
58388     },
58389
58390     /**
58391      * Returns the grid's DataSource.
58392      * @return {DataSource}
58393      */
58394     getDataSource : function(){
58395         return this.dataSource;
58396     },
58397
58398     /**
58399      * Returns the grid's ColumnModel.
58400      * @return {ColumnModel}
58401      */
58402     getColumnModel : function(){
58403         return this.colModel;
58404     },
58405
58406     /**
58407      * Returns the grid's GridView object.
58408      * @return {GridView}
58409      */
58410     getView : function(){
58411         if(!this.view){
58412             this.view = new Roo.grid.GridView(this.viewConfig);
58413             this.relayEvents(this.view, [
58414                 "beforerowremoved", "beforerowsinserted",
58415                 "beforerefresh", "rowremoved",
58416                 "rowsinserted", "rowupdated" ,"refresh"
58417             ]);
58418         }
58419         return this.view;
58420     },
58421     /**
58422      * Called to get grid's drag proxy text, by default returns this.ddText.
58423      * Override this to put something different in the dragged text.
58424      * @return {String}
58425      */
58426     getDragDropText : function(){
58427         var count = this.selModel.getCount();
58428         return String.format(this.ddText, count, count == 1 ? '' : 's');
58429     }
58430 });
58431 /*
58432  * Based on:
58433  * Ext JS Library 1.1.1
58434  * Copyright(c) 2006-2007, Ext JS, LLC.
58435  *
58436  * Originally Released Under LGPL - original licence link has changed is not relivant.
58437  *
58438  * Fork - LGPL
58439  * <script type="text/javascript">
58440  */
58441  /**
58442  * @class Roo.grid.AbstractGridView
58443  * @extends Roo.util.Observable
58444  * @abstract
58445  * Abstract base class for grid Views
58446  * @constructor
58447  */
58448 Roo.grid.AbstractGridView = function(){
58449         this.grid = null;
58450         
58451         this.events = {
58452             "beforerowremoved" : true,
58453             "beforerowsinserted" : true,
58454             "beforerefresh" : true,
58455             "rowremoved" : true,
58456             "rowsinserted" : true,
58457             "rowupdated" : true,
58458             "refresh" : true
58459         };
58460     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58461 };
58462
58463 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58464     rowClass : "x-grid-row",
58465     cellClass : "x-grid-cell",
58466     tdClass : "x-grid-td",
58467     hdClass : "x-grid-hd",
58468     splitClass : "x-grid-hd-split",
58469     
58470     init: function(grid){
58471         this.grid = grid;
58472                 var cid = this.grid.getGridEl().id;
58473         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58474         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58475         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58476         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58477         },
58478         
58479     getColumnRenderers : function(){
58480         var renderers = [];
58481         var cm = this.grid.colModel;
58482         var colCount = cm.getColumnCount();
58483         for(var i = 0; i < colCount; i++){
58484             renderers[i] = cm.getRenderer(i);
58485         }
58486         return renderers;
58487     },
58488     
58489     getColumnIds : function(){
58490         var ids = [];
58491         var cm = this.grid.colModel;
58492         var colCount = cm.getColumnCount();
58493         for(var i = 0; i < colCount; i++){
58494             ids[i] = cm.getColumnId(i);
58495         }
58496         return ids;
58497     },
58498     
58499     getDataIndexes : function(){
58500         if(!this.indexMap){
58501             this.indexMap = this.buildIndexMap();
58502         }
58503         return this.indexMap.colToData;
58504     },
58505     
58506     getColumnIndexByDataIndex : function(dataIndex){
58507         if(!this.indexMap){
58508             this.indexMap = this.buildIndexMap();
58509         }
58510         return this.indexMap.dataToCol[dataIndex];
58511     },
58512     
58513     /**
58514      * Set a css style for a column dynamically. 
58515      * @param {Number} colIndex The index of the column
58516      * @param {String} name The css property name
58517      * @param {String} value The css value
58518      */
58519     setCSSStyle : function(colIndex, name, value){
58520         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58521         Roo.util.CSS.updateRule(selector, name, value);
58522     },
58523     
58524     generateRules : function(cm){
58525         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58526         Roo.util.CSS.removeStyleSheet(rulesId);
58527         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58528             var cid = cm.getColumnId(i);
58529             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58530                          this.tdSelector, cid, " {\n}\n",
58531                          this.hdSelector, cid, " {\n}\n",
58532                          this.splitSelector, cid, " {\n}\n");
58533         }
58534         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58535     }
58536 });/*
58537  * Based on:
58538  * Ext JS Library 1.1.1
58539  * Copyright(c) 2006-2007, Ext JS, LLC.
58540  *
58541  * Originally Released Under LGPL - original licence link has changed is not relivant.
58542  *
58543  * Fork - LGPL
58544  * <script type="text/javascript">
58545  */
58546
58547 // private
58548 // This is a support class used internally by the Grid components
58549 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58550     this.grid = grid;
58551     this.view = grid.getView();
58552     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58553     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58554     if(hd2){
58555         this.setHandleElId(Roo.id(hd));
58556         this.setOuterHandleElId(Roo.id(hd2));
58557     }
58558     this.scroll = false;
58559 };
58560 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58561     maxDragWidth: 120,
58562     getDragData : function(e){
58563         var t = Roo.lib.Event.getTarget(e);
58564         var h = this.view.findHeaderCell(t);
58565         if(h){
58566             return {ddel: h.firstChild, header:h};
58567         }
58568         return false;
58569     },
58570
58571     onInitDrag : function(e){
58572         this.view.headersDisabled = true;
58573         var clone = this.dragData.ddel.cloneNode(true);
58574         clone.id = Roo.id();
58575         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58576         this.proxy.update(clone);
58577         return true;
58578     },
58579
58580     afterValidDrop : function(){
58581         var v = this.view;
58582         setTimeout(function(){
58583             v.headersDisabled = false;
58584         }, 50);
58585     },
58586
58587     afterInvalidDrop : function(){
58588         var v = this.view;
58589         setTimeout(function(){
58590             v.headersDisabled = false;
58591         }, 50);
58592     }
58593 });
58594 /*
58595  * Based on:
58596  * Ext JS Library 1.1.1
58597  * Copyright(c) 2006-2007, Ext JS, LLC.
58598  *
58599  * Originally Released Under LGPL - original licence link has changed is not relivant.
58600  *
58601  * Fork - LGPL
58602  * <script type="text/javascript">
58603  */
58604 // private
58605 // This is a support class used internally by the Grid components
58606 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58607     this.grid = grid;
58608     this.view = grid.getView();
58609     // split the proxies so they don't interfere with mouse events
58610     this.proxyTop = Roo.DomHelper.append(document.body, {
58611         cls:"col-move-top", html:"&#160;"
58612     }, true);
58613     this.proxyBottom = Roo.DomHelper.append(document.body, {
58614         cls:"col-move-bottom", html:"&#160;"
58615     }, true);
58616     this.proxyTop.hide = this.proxyBottom.hide = function(){
58617         this.setLeftTop(-100,-100);
58618         this.setStyle("visibility", "hidden");
58619     };
58620     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58621     // temporarily disabled
58622     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58623     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58624 };
58625 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58626     proxyOffsets : [-4, -9],
58627     fly: Roo.Element.fly,
58628
58629     getTargetFromEvent : function(e){
58630         var t = Roo.lib.Event.getTarget(e);
58631         var cindex = this.view.findCellIndex(t);
58632         if(cindex !== false){
58633             return this.view.getHeaderCell(cindex);
58634         }
58635         return null;
58636     },
58637
58638     nextVisible : function(h){
58639         var v = this.view, cm = this.grid.colModel;
58640         h = h.nextSibling;
58641         while(h){
58642             if(!cm.isHidden(v.getCellIndex(h))){
58643                 return h;
58644             }
58645             h = h.nextSibling;
58646         }
58647         return null;
58648     },
58649
58650     prevVisible : function(h){
58651         var v = this.view, cm = this.grid.colModel;
58652         h = h.prevSibling;
58653         while(h){
58654             if(!cm.isHidden(v.getCellIndex(h))){
58655                 return h;
58656             }
58657             h = h.prevSibling;
58658         }
58659         return null;
58660     },
58661
58662     positionIndicator : function(h, n, e){
58663         var x = Roo.lib.Event.getPageX(e);
58664         var r = Roo.lib.Dom.getRegion(n.firstChild);
58665         var px, pt, py = r.top + this.proxyOffsets[1];
58666         if((r.right - x) <= (r.right-r.left)/2){
58667             px = r.right+this.view.borderWidth;
58668             pt = "after";
58669         }else{
58670             px = r.left;
58671             pt = "before";
58672         }
58673         var oldIndex = this.view.getCellIndex(h);
58674         var newIndex = this.view.getCellIndex(n);
58675
58676         if(this.grid.colModel.isFixed(newIndex)){
58677             return false;
58678         }
58679
58680         var locked = this.grid.colModel.isLocked(newIndex);
58681
58682         if(pt == "after"){
58683             newIndex++;
58684         }
58685         if(oldIndex < newIndex){
58686             newIndex--;
58687         }
58688         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58689             return false;
58690         }
58691         px +=  this.proxyOffsets[0];
58692         this.proxyTop.setLeftTop(px, py);
58693         this.proxyTop.show();
58694         if(!this.bottomOffset){
58695             this.bottomOffset = this.view.mainHd.getHeight();
58696         }
58697         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58698         this.proxyBottom.show();
58699         return pt;
58700     },
58701
58702     onNodeEnter : function(n, dd, e, data){
58703         if(data.header != n){
58704             this.positionIndicator(data.header, n, e);
58705         }
58706     },
58707
58708     onNodeOver : function(n, dd, e, data){
58709         var result = false;
58710         if(data.header != n){
58711             result = this.positionIndicator(data.header, n, e);
58712         }
58713         if(!result){
58714             this.proxyTop.hide();
58715             this.proxyBottom.hide();
58716         }
58717         return result ? this.dropAllowed : this.dropNotAllowed;
58718     },
58719
58720     onNodeOut : function(n, dd, e, data){
58721         this.proxyTop.hide();
58722         this.proxyBottom.hide();
58723     },
58724
58725     onNodeDrop : function(n, dd, e, data){
58726         var h = data.header;
58727         if(h != n){
58728             var cm = this.grid.colModel;
58729             var x = Roo.lib.Event.getPageX(e);
58730             var r = Roo.lib.Dom.getRegion(n.firstChild);
58731             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58732             var oldIndex = this.view.getCellIndex(h);
58733             var newIndex = this.view.getCellIndex(n);
58734             var locked = cm.isLocked(newIndex);
58735             if(pt == "after"){
58736                 newIndex++;
58737             }
58738             if(oldIndex < newIndex){
58739                 newIndex--;
58740             }
58741             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58742                 return false;
58743             }
58744             cm.setLocked(oldIndex, locked, true);
58745             cm.moveColumn(oldIndex, newIndex);
58746             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58747             return true;
58748         }
58749         return false;
58750     }
58751 });
58752 /*
58753  * Based on:
58754  * Ext JS Library 1.1.1
58755  * Copyright(c) 2006-2007, Ext JS, LLC.
58756  *
58757  * Originally Released Under LGPL - original licence link has changed is not relivant.
58758  *
58759  * Fork - LGPL
58760  * <script type="text/javascript">
58761  */
58762   
58763 /**
58764  * @class Roo.grid.GridView
58765  * @extends Roo.util.Observable
58766  *
58767  * @constructor
58768  * @param {Object} config
58769  */
58770 Roo.grid.GridView = function(config){
58771     Roo.grid.GridView.superclass.constructor.call(this);
58772     this.el = null;
58773
58774     Roo.apply(this, config);
58775 };
58776
58777 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58778
58779     unselectable :  'unselectable="on"',
58780     unselectableCls :  'x-unselectable',
58781     
58782     
58783     rowClass : "x-grid-row",
58784
58785     cellClass : "x-grid-col",
58786
58787     tdClass : "x-grid-td",
58788
58789     hdClass : "x-grid-hd",
58790
58791     splitClass : "x-grid-split",
58792
58793     sortClasses : ["sort-asc", "sort-desc"],
58794
58795     enableMoveAnim : false,
58796
58797     hlColor: "C3DAF9",
58798
58799     dh : Roo.DomHelper,
58800
58801     fly : Roo.Element.fly,
58802
58803     css : Roo.util.CSS,
58804
58805     borderWidth: 1,
58806
58807     splitOffset: 3,
58808
58809     scrollIncrement : 22,
58810
58811     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58812
58813     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58814
58815     bind : function(ds, cm){
58816         if(this.ds){
58817             this.ds.un("load", this.onLoad, this);
58818             this.ds.un("datachanged", this.onDataChange, this);
58819             this.ds.un("add", this.onAdd, this);
58820             this.ds.un("remove", this.onRemove, this);
58821             this.ds.un("update", this.onUpdate, this);
58822             this.ds.un("clear", this.onClear, this);
58823         }
58824         if(ds){
58825             ds.on("load", this.onLoad, this);
58826             ds.on("datachanged", this.onDataChange, this);
58827             ds.on("add", this.onAdd, this);
58828             ds.on("remove", this.onRemove, this);
58829             ds.on("update", this.onUpdate, this);
58830             ds.on("clear", this.onClear, this);
58831         }
58832         this.ds = ds;
58833
58834         if(this.cm){
58835             this.cm.un("widthchange", this.onColWidthChange, this);
58836             this.cm.un("headerchange", this.onHeaderChange, this);
58837             this.cm.un("hiddenchange", this.onHiddenChange, this);
58838             this.cm.un("columnmoved", this.onColumnMove, this);
58839             this.cm.un("columnlockchange", this.onColumnLock, this);
58840         }
58841         if(cm){
58842             this.generateRules(cm);
58843             cm.on("widthchange", this.onColWidthChange, this);
58844             cm.on("headerchange", this.onHeaderChange, this);
58845             cm.on("hiddenchange", this.onHiddenChange, this);
58846             cm.on("columnmoved", this.onColumnMove, this);
58847             cm.on("columnlockchange", this.onColumnLock, this);
58848         }
58849         this.cm = cm;
58850     },
58851
58852     init: function(grid){
58853         Roo.grid.GridView.superclass.init.call(this, grid);
58854
58855         this.bind(grid.dataSource, grid.colModel);
58856
58857         grid.on("headerclick", this.handleHeaderClick, this);
58858
58859         if(grid.trackMouseOver){
58860             grid.on("mouseover", this.onRowOver, this);
58861             grid.on("mouseout", this.onRowOut, this);
58862         }
58863         grid.cancelTextSelection = function(){};
58864         this.gridId = grid.id;
58865
58866         var tpls = this.templates || {};
58867
58868         if(!tpls.master){
58869             tpls.master = new Roo.Template(
58870                '<div class="x-grid" hidefocus="true">',
58871                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58872                   '<div class="x-grid-topbar"></div>',
58873                   '<div class="x-grid-scroller"><div></div></div>',
58874                   '<div class="x-grid-locked">',
58875                       '<div class="x-grid-header">{lockedHeader}</div>',
58876                       '<div class="x-grid-body">{lockedBody}</div>',
58877                   "</div>",
58878                   '<div class="x-grid-viewport">',
58879                       '<div class="x-grid-header">{header}</div>',
58880                       '<div class="x-grid-body">{body}</div>',
58881                   "</div>",
58882                   '<div class="x-grid-bottombar"></div>',
58883                  
58884                   '<div class="x-grid-resize-proxy">&#160;</div>',
58885                "</div>"
58886             );
58887             tpls.master.disableformats = true;
58888         }
58889
58890         if(!tpls.header){
58891             tpls.header = new Roo.Template(
58892                '<table border="0" cellspacing="0" cellpadding="0">',
58893                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58894                "</table>{splits}"
58895             );
58896             tpls.header.disableformats = true;
58897         }
58898         tpls.header.compile();
58899
58900         if(!tpls.hcell){
58901             tpls.hcell = new Roo.Template(
58902                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58903                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58904                 "</div></td>"
58905              );
58906              tpls.hcell.disableFormats = true;
58907         }
58908         tpls.hcell.compile();
58909
58910         if(!tpls.hsplit){
58911             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58912                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58913             tpls.hsplit.disableFormats = true;
58914         }
58915         tpls.hsplit.compile();
58916
58917         if(!tpls.body){
58918             tpls.body = new Roo.Template(
58919                '<table border="0" cellspacing="0" cellpadding="0">',
58920                "<tbody>{rows}</tbody>",
58921                "</table>"
58922             );
58923             tpls.body.disableFormats = true;
58924         }
58925         tpls.body.compile();
58926
58927         if(!tpls.row){
58928             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58929             tpls.row.disableFormats = true;
58930         }
58931         tpls.row.compile();
58932
58933         if(!tpls.cell){
58934             tpls.cell = new Roo.Template(
58935                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58936                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58937                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58938                 "</td>"
58939             );
58940             tpls.cell.disableFormats = true;
58941         }
58942         tpls.cell.compile();
58943
58944         this.templates = tpls;
58945     },
58946
58947     // remap these for backwards compat
58948     onColWidthChange : function(){
58949         this.updateColumns.apply(this, arguments);
58950     },
58951     onHeaderChange : function(){
58952         this.updateHeaders.apply(this, arguments);
58953     }, 
58954     onHiddenChange : function(){
58955         this.handleHiddenChange.apply(this, arguments);
58956     },
58957     onColumnMove : function(){
58958         this.handleColumnMove.apply(this, arguments);
58959     },
58960     onColumnLock : function(){
58961         this.handleLockChange.apply(this, arguments);
58962     },
58963
58964     onDataChange : function(){
58965         this.refresh();
58966         this.updateHeaderSortState();
58967     },
58968
58969     onClear : function(){
58970         this.refresh();
58971     },
58972
58973     onUpdate : function(ds, record){
58974         this.refreshRow(record);
58975     },
58976
58977     refreshRow : function(record){
58978         var ds = this.ds, index;
58979         if(typeof record == 'number'){
58980             index = record;
58981             record = ds.getAt(index);
58982         }else{
58983             index = ds.indexOf(record);
58984         }
58985         this.insertRows(ds, index, index, true);
58986         this.onRemove(ds, record, index+1, true);
58987         this.syncRowHeights(index, index);
58988         this.layout();
58989         this.fireEvent("rowupdated", this, index, record);
58990     },
58991
58992     onAdd : function(ds, records, index){
58993         this.insertRows(ds, index, index + (records.length-1));
58994     },
58995
58996     onRemove : function(ds, record, index, isUpdate){
58997         if(isUpdate !== true){
58998             this.fireEvent("beforerowremoved", this, index, record);
58999         }
59000         var bt = this.getBodyTable(), lt = this.getLockedTable();
59001         if(bt.rows[index]){
59002             bt.firstChild.removeChild(bt.rows[index]);
59003         }
59004         if(lt.rows[index]){
59005             lt.firstChild.removeChild(lt.rows[index]);
59006         }
59007         if(isUpdate !== true){
59008             this.stripeRows(index);
59009             this.syncRowHeights(index, index);
59010             this.layout();
59011             this.fireEvent("rowremoved", this, index, record);
59012         }
59013     },
59014
59015     onLoad : function(){
59016         this.scrollToTop();
59017     },
59018
59019     /**
59020      * Scrolls the grid to the top
59021      */
59022     scrollToTop : function(){
59023         if(this.scroller){
59024             this.scroller.dom.scrollTop = 0;
59025             this.syncScroll();
59026         }
59027     },
59028
59029     /**
59030      * Gets a panel in the header of the grid that can be used for toolbars etc.
59031      * After modifying the contents of this panel a call to grid.autoSize() may be
59032      * required to register any changes in size.
59033      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59034      * @return Roo.Element
59035      */
59036     getHeaderPanel : function(doShow){
59037         if(doShow){
59038             this.headerPanel.show();
59039         }
59040         return this.headerPanel;
59041     },
59042
59043     /**
59044      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59045      * After modifying the contents of this panel a call to grid.autoSize() may be
59046      * required to register any changes in size.
59047      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59048      * @return Roo.Element
59049      */
59050     getFooterPanel : function(doShow){
59051         if(doShow){
59052             this.footerPanel.show();
59053         }
59054         return this.footerPanel;
59055     },
59056
59057     initElements : function(){
59058         var E = Roo.Element;
59059         var el = this.grid.getGridEl().dom.firstChild;
59060         var cs = el.childNodes;
59061
59062         this.el = new E(el);
59063         
59064          this.focusEl = new E(el.firstChild);
59065         this.focusEl.swallowEvent("click", true);
59066         
59067         this.headerPanel = new E(cs[1]);
59068         this.headerPanel.enableDisplayMode("block");
59069
59070         this.scroller = new E(cs[2]);
59071         this.scrollSizer = new E(this.scroller.dom.firstChild);
59072
59073         this.lockedWrap = new E(cs[3]);
59074         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59075         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59076
59077         this.mainWrap = new E(cs[4]);
59078         this.mainHd = new E(this.mainWrap.dom.firstChild);
59079         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59080
59081         this.footerPanel = new E(cs[5]);
59082         this.footerPanel.enableDisplayMode("block");
59083
59084         this.resizeProxy = new E(cs[6]);
59085
59086         this.headerSelector = String.format(
59087            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59088            this.lockedHd.id, this.mainHd.id
59089         );
59090
59091         this.splitterSelector = String.format(
59092            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59093            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59094         );
59095     },
59096     idToCssName : function(s)
59097     {
59098         return s.replace(/[^a-z0-9]+/ig, '-');
59099     },
59100
59101     getHeaderCell : function(index){
59102         return Roo.DomQuery.select(this.headerSelector)[index];
59103     },
59104
59105     getHeaderCellMeasure : function(index){
59106         return this.getHeaderCell(index).firstChild;
59107     },
59108
59109     getHeaderCellText : function(index){
59110         return this.getHeaderCell(index).firstChild.firstChild;
59111     },
59112
59113     getLockedTable : function(){
59114         return this.lockedBody.dom.firstChild;
59115     },
59116
59117     getBodyTable : function(){
59118         return this.mainBody.dom.firstChild;
59119     },
59120
59121     getLockedRow : function(index){
59122         return this.getLockedTable().rows[index];
59123     },
59124
59125     getRow : function(index){
59126         return this.getBodyTable().rows[index];
59127     },
59128
59129     getRowComposite : function(index){
59130         if(!this.rowEl){
59131             this.rowEl = new Roo.CompositeElementLite();
59132         }
59133         var els = [], lrow, mrow;
59134         if(lrow = this.getLockedRow(index)){
59135             els.push(lrow);
59136         }
59137         if(mrow = this.getRow(index)){
59138             els.push(mrow);
59139         }
59140         this.rowEl.elements = els;
59141         return this.rowEl;
59142     },
59143     /**
59144      * Gets the 'td' of the cell
59145      * 
59146      * @param {Integer} rowIndex row to select
59147      * @param {Integer} colIndex column to select
59148      * 
59149      * @return {Object} 
59150      */
59151     getCell : function(rowIndex, colIndex){
59152         var locked = this.cm.getLockedCount();
59153         var source;
59154         if(colIndex < locked){
59155             source = this.lockedBody.dom.firstChild;
59156         }else{
59157             source = this.mainBody.dom.firstChild;
59158             colIndex -= locked;
59159         }
59160         return source.rows[rowIndex].childNodes[colIndex];
59161     },
59162
59163     getCellText : function(rowIndex, colIndex){
59164         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59165     },
59166
59167     getCellBox : function(cell){
59168         var b = this.fly(cell).getBox();
59169         if(Roo.isOpera){ // opera fails to report the Y
59170             b.y = cell.offsetTop + this.mainBody.getY();
59171         }
59172         return b;
59173     },
59174
59175     getCellIndex : function(cell){
59176         var id = String(cell.className).match(this.cellRE);
59177         if(id){
59178             return parseInt(id[1], 10);
59179         }
59180         return 0;
59181     },
59182
59183     findHeaderIndex : function(n){
59184         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59185         return r ? this.getCellIndex(r) : false;
59186     },
59187
59188     findHeaderCell : function(n){
59189         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59190         return r ? r : false;
59191     },
59192
59193     findRowIndex : function(n){
59194         if(!n){
59195             return false;
59196         }
59197         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59198         return r ? r.rowIndex : false;
59199     },
59200
59201     findCellIndex : function(node){
59202         var stop = this.el.dom;
59203         while(node && node != stop){
59204             if(this.findRE.test(node.className)){
59205                 return this.getCellIndex(node);
59206             }
59207             node = node.parentNode;
59208         }
59209         return false;
59210     },
59211
59212     getColumnId : function(index){
59213         return this.cm.getColumnId(index);
59214     },
59215
59216     getSplitters : function()
59217     {
59218         if(this.splitterSelector){
59219            return Roo.DomQuery.select(this.splitterSelector);
59220         }else{
59221             return null;
59222       }
59223     },
59224
59225     getSplitter : function(index){
59226         return this.getSplitters()[index];
59227     },
59228
59229     onRowOver : function(e, t){
59230         var row;
59231         if((row = this.findRowIndex(t)) !== false){
59232             this.getRowComposite(row).addClass("x-grid-row-over");
59233         }
59234     },
59235
59236     onRowOut : function(e, t){
59237         var row;
59238         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59239             this.getRowComposite(row).removeClass("x-grid-row-over");
59240         }
59241     },
59242
59243     renderHeaders : function(){
59244         var cm = this.cm;
59245         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59246         var cb = [], lb = [], sb = [], lsb = [], p = {};
59247         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59248             p.cellId = "x-grid-hd-0-" + i;
59249             p.splitId = "x-grid-csplit-0-" + i;
59250             p.id = cm.getColumnId(i);
59251             p.value = cm.getColumnHeader(i) || "";
59252             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59253             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59254             if(!cm.isLocked(i)){
59255                 cb[cb.length] = ct.apply(p);
59256                 sb[sb.length] = st.apply(p);
59257             }else{
59258                 lb[lb.length] = ct.apply(p);
59259                 lsb[lsb.length] = st.apply(p);
59260             }
59261         }
59262         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59263                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59264     },
59265
59266     updateHeaders : function(){
59267         var html = this.renderHeaders();
59268         this.lockedHd.update(html[0]);
59269         this.mainHd.update(html[1]);
59270     },
59271
59272     /**
59273      * Focuses the specified row.
59274      * @param {Number} row The row index
59275      */
59276     focusRow : function(row)
59277     {
59278         //Roo.log('GridView.focusRow');
59279         var x = this.scroller.dom.scrollLeft;
59280         this.focusCell(row, 0, false);
59281         this.scroller.dom.scrollLeft = x;
59282     },
59283
59284     /**
59285      * Focuses the specified cell.
59286      * @param {Number} row The row index
59287      * @param {Number} col The column index
59288      * @param {Boolean} hscroll false to disable horizontal scrolling
59289      */
59290     focusCell : function(row, col, hscroll)
59291     {
59292         //Roo.log('GridView.focusCell');
59293         var el = this.ensureVisible(row, col, hscroll);
59294         this.focusEl.alignTo(el, "tl-tl");
59295         if(Roo.isGecko){
59296             this.focusEl.focus();
59297         }else{
59298             this.focusEl.focus.defer(1, this.focusEl);
59299         }
59300     },
59301
59302     /**
59303      * Scrolls the specified cell into view
59304      * @param {Number} row The row index
59305      * @param {Number} col The column index
59306      * @param {Boolean} hscroll false to disable horizontal scrolling
59307      */
59308     ensureVisible : function(row, col, hscroll)
59309     {
59310         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59311         //return null; //disable for testing.
59312         if(typeof row != "number"){
59313             row = row.rowIndex;
59314         }
59315         if(row < 0 && row >= this.ds.getCount()){
59316             return  null;
59317         }
59318         col = (col !== undefined ? col : 0);
59319         var cm = this.grid.colModel;
59320         while(cm.isHidden(col)){
59321             col++;
59322         }
59323
59324         var el = this.getCell(row, col);
59325         if(!el){
59326             return null;
59327         }
59328         var c = this.scroller.dom;
59329
59330         var ctop = parseInt(el.offsetTop, 10);
59331         var cleft = parseInt(el.offsetLeft, 10);
59332         var cbot = ctop + el.offsetHeight;
59333         var cright = cleft + el.offsetWidth;
59334         
59335         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59336         var stop = parseInt(c.scrollTop, 10);
59337         var sleft = parseInt(c.scrollLeft, 10);
59338         var sbot = stop + ch;
59339         var sright = sleft + c.clientWidth;
59340         /*
59341         Roo.log('GridView.ensureVisible:' +
59342                 ' ctop:' + ctop +
59343                 ' c.clientHeight:' + c.clientHeight +
59344                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59345                 ' stop:' + stop +
59346                 ' cbot:' + cbot +
59347                 ' sbot:' + sbot +
59348                 ' ch:' + ch  
59349                 );
59350         */
59351         if(ctop < stop){
59352             c.scrollTop = ctop;
59353             //Roo.log("set scrolltop to ctop DISABLE?");
59354         }else if(cbot > sbot){
59355             //Roo.log("set scrolltop to cbot-ch");
59356             c.scrollTop = cbot-ch;
59357         }
59358         
59359         if(hscroll !== false){
59360             if(cleft < sleft){
59361                 c.scrollLeft = cleft;
59362             }else if(cright > sright){
59363                 c.scrollLeft = cright-c.clientWidth;
59364             }
59365         }
59366          
59367         return el;
59368     },
59369
59370     updateColumns : function(){
59371         this.grid.stopEditing();
59372         var cm = this.grid.colModel, colIds = this.getColumnIds();
59373         //var totalWidth = cm.getTotalWidth();
59374         var pos = 0;
59375         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59376             //if(cm.isHidden(i)) continue;
59377             var w = cm.getColumnWidth(i);
59378             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59379             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59380         }
59381         this.updateSplitters();
59382     },
59383
59384     generateRules : function(cm){
59385         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59386         Roo.util.CSS.removeStyleSheet(rulesId);
59387         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59388             var cid = cm.getColumnId(i);
59389             var align = '';
59390             if(cm.config[i].align){
59391                 align = 'text-align:'+cm.config[i].align+';';
59392             }
59393             var hidden = '';
59394             if(cm.isHidden(i)){
59395                 hidden = 'display:none;';
59396             }
59397             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59398             ruleBuf.push(
59399                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59400                     this.hdSelector, cid, " {\n", align, width, "}\n",
59401                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59402                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59403         }
59404         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59405     },
59406
59407     updateSplitters : function(){
59408         var cm = this.cm, s = this.getSplitters();
59409         if(s){ // splitters not created yet
59410             var pos = 0, locked = true;
59411             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59412                 if(cm.isHidden(i)) {
59413                     continue;
59414                 }
59415                 var w = cm.getColumnWidth(i); // make sure it's a number
59416                 if(!cm.isLocked(i) && locked){
59417                     pos = 0;
59418                     locked = false;
59419                 }
59420                 pos += w;
59421                 s[i].style.left = (pos-this.splitOffset) + "px";
59422             }
59423         }
59424     },
59425
59426     handleHiddenChange : function(colModel, colIndex, hidden){
59427         if(hidden){
59428             this.hideColumn(colIndex);
59429         }else{
59430             this.unhideColumn(colIndex);
59431         }
59432     },
59433
59434     hideColumn : function(colIndex){
59435         var cid = this.getColumnId(colIndex);
59436         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59437         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59438         if(Roo.isSafari){
59439             this.updateHeaders();
59440         }
59441         this.updateSplitters();
59442         this.layout();
59443     },
59444
59445     unhideColumn : function(colIndex){
59446         var cid = this.getColumnId(colIndex);
59447         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59448         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59449
59450         if(Roo.isSafari){
59451             this.updateHeaders();
59452         }
59453         this.updateSplitters();
59454         this.layout();
59455     },
59456
59457     insertRows : function(dm, firstRow, lastRow, isUpdate){
59458         if(firstRow == 0 && lastRow == dm.getCount()-1){
59459             this.refresh();
59460         }else{
59461             if(!isUpdate){
59462                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59463             }
59464             var s = this.getScrollState();
59465             var markup = this.renderRows(firstRow, lastRow);
59466             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59467             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59468             this.restoreScroll(s);
59469             if(!isUpdate){
59470                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59471                 this.syncRowHeights(firstRow, lastRow);
59472                 this.stripeRows(firstRow);
59473                 this.layout();
59474             }
59475         }
59476     },
59477
59478     bufferRows : function(markup, target, index){
59479         var before = null, trows = target.rows, tbody = target.tBodies[0];
59480         if(index < trows.length){
59481             before = trows[index];
59482         }
59483         var b = document.createElement("div");
59484         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59485         var rows = b.firstChild.rows;
59486         for(var i = 0, len = rows.length; i < len; i++){
59487             if(before){
59488                 tbody.insertBefore(rows[0], before);
59489             }else{
59490                 tbody.appendChild(rows[0]);
59491             }
59492         }
59493         b.innerHTML = "";
59494         b = null;
59495     },
59496
59497     deleteRows : function(dm, firstRow, lastRow){
59498         if(dm.getRowCount()<1){
59499             this.fireEvent("beforerefresh", this);
59500             this.mainBody.update("");
59501             this.lockedBody.update("");
59502             this.fireEvent("refresh", this);
59503         }else{
59504             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59505             var bt = this.getBodyTable();
59506             var tbody = bt.firstChild;
59507             var rows = bt.rows;
59508             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59509                 tbody.removeChild(rows[firstRow]);
59510             }
59511             this.stripeRows(firstRow);
59512             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59513         }
59514     },
59515
59516     updateRows : function(dataSource, firstRow, lastRow){
59517         var s = this.getScrollState();
59518         this.refresh();
59519         this.restoreScroll(s);
59520     },
59521
59522     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59523         if(!noRefresh){
59524            this.refresh();
59525         }
59526         this.updateHeaderSortState();
59527     },
59528
59529     getScrollState : function(){
59530         
59531         var sb = this.scroller.dom;
59532         return {left: sb.scrollLeft, top: sb.scrollTop};
59533     },
59534
59535     stripeRows : function(startRow){
59536         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59537             return;
59538         }
59539         startRow = startRow || 0;
59540         var rows = this.getBodyTable().rows;
59541         var lrows = this.getLockedTable().rows;
59542         var cls = ' x-grid-row-alt ';
59543         for(var i = startRow, len = rows.length; i < len; i++){
59544             var row = rows[i], lrow = lrows[i];
59545             var isAlt = ((i+1) % 2 == 0);
59546             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59547             if(isAlt == hasAlt){
59548                 continue;
59549             }
59550             if(isAlt){
59551                 row.className += " x-grid-row-alt";
59552             }else{
59553                 row.className = row.className.replace("x-grid-row-alt", "");
59554             }
59555             if(lrow){
59556                 lrow.className = row.className;
59557             }
59558         }
59559     },
59560
59561     restoreScroll : function(state){
59562         //Roo.log('GridView.restoreScroll');
59563         var sb = this.scroller.dom;
59564         sb.scrollLeft = state.left;
59565         sb.scrollTop = state.top;
59566         this.syncScroll();
59567     },
59568
59569     syncScroll : function(){
59570         //Roo.log('GridView.syncScroll');
59571         var sb = this.scroller.dom;
59572         var sh = this.mainHd.dom;
59573         var bs = this.mainBody.dom;
59574         var lv = this.lockedBody.dom;
59575         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59576         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59577     },
59578
59579     handleScroll : function(e){
59580         this.syncScroll();
59581         var sb = this.scroller.dom;
59582         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59583         e.stopEvent();
59584     },
59585
59586     handleWheel : function(e){
59587         var d = e.getWheelDelta();
59588         this.scroller.dom.scrollTop -= d*22;
59589         // set this here to prevent jumpy scrolling on large tables
59590         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59591         e.stopEvent();
59592     },
59593
59594     renderRows : function(startRow, endRow){
59595         // pull in all the crap needed to render rows
59596         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59597         var colCount = cm.getColumnCount();
59598
59599         if(ds.getCount() < 1){
59600             return ["", ""];
59601         }
59602
59603         // build a map for all the columns
59604         var cs = [];
59605         for(var i = 0; i < colCount; i++){
59606             var name = cm.getDataIndex(i);
59607             cs[i] = {
59608                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59609                 renderer : cm.getRenderer(i),
59610                 id : cm.getColumnId(i),
59611                 locked : cm.isLocked(i),
59612                 has_editor : cm.isCellEditable(i)
59613             };
59614         }
59615
59616         startRow = startRow || 0;
59617         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59618
59619         // records to render
59620         var rs = ds.getRange(startRow, endRow);
59621
59622         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59623     },
59624
59625     // As much as I hate to duplicate code, this was branched because FireFox really hates
59626     // [].join("") on strings. The performance difference was substantial enough to
59627     // branch this function
59628     doRender : Roo.isGecko ?
59629             function(cs, rs, ds, startRow, colCount, stripe){
59630                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59631                 // buffers
59632                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59633                 
59634                 var hasListener = this.grid.hasListener('rowclass');
59635                 var rowcfg = {};
59636                 for(var j = 0, len = rs.length; j < len; j++){
59637                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59638                     for(var i = 0; i < colCount; i++){
59639                         c = cs[i];
59640                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59641                         p.id = c.id;
59642                         p.css = p.attr = "";
59643                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59644                         if(p.value == undefined || p.value === "") {
59645                             p.value = "&#160;";
59646                         }
59647                         if(c.has_editor){
59648                             p.css += ' x-grid-editable-cell';
59649                         }
59650                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59651                             p.css +=  ' x-grid-dirty-cell';
59652                         }
59653                         var markup = ct.apply(p);
59654                         if(!c.locked){
59655                             cb+= markup;
59656                         }else{
59657                             lcb+= markup;
59658                         }
59659                     }
59660                     var alt = [];
59661                     if(stripe && ((rowIndex+1) % 2 == 0)){
59662                         alt.push("x-grid-row-alt")
59663                     }
59664                     if(r.dirty){
59665                         alt.push(  " x-grid-dirty-row");
59666                     }
59667                     rp.cells = lcb;
59668                     if(this.getRowClass){
59669                         alt.push(this.getRowClass(r, rowIndex));
59670                     }
59671                     if (hasListener) {
59672                         rowcfg = {
59673                              
59674                             record: r,
59675                             rowIndex : rowIndex,
59676                             rowClass : ''
59677                         };
59678                         this.grid.fireEvent('rowclass', this, rowcfg);
59679                         alt.push(rowcfg.rowClass);
59680                     }
59681                     rp.alt = alt.join(" ");
59682                     lbuf+= rt.apply(rp);
59683                     rp.cells = cb;
59684                     buf+=  rt.apply(rp);
59685                 }
59686                 return [lbuf, buf];
59687             } :
59688             function(cs, rs, ds, startRow, colCount, stripe){
59689                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59690                 // buffers
59691                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59692                 var hasListener = this.grid.hasListener('rowclass');
59693  
59694                 var rowcfg = {};
59695                 for(var j = 0, len = rs.length; j < len; j++){
59696                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59697                     for(var i = 0; i < colCount; i++){
59698                         c = cs[i];
59699                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59700                         p.id = c.id;
59701                         p.css = p.attr = "";
59702                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59703                         if(p.value == undefined || p.value === "") {
59704                             p.value = "&#160;";
59705                         }
59706                         //Roo.log(c);
59707                          if(c.has_editor){
59708                             p.css += ' x-grid-editable-cell';
59709                         }
59710                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59711                             p.css += ' x-grid-dirty-cell' 
59712                         }
59713                         
59714                         var markup = ct.apply(p);
59715                         if(!c.locked){
59716                             cb[cb.length] = markup;
59717                         }else{
59718                             lcb[lcb.length] = markup;
59719                         }
59720                     }
59721                     var alt = [];
59722                     if(stripe && ((rowIndex+1) % 2 == 0)){
59723                         alt.push( "x-grid-row-alt");
59724                     }
59725                     if(r.dirty){
59726                         alt.push(" x-grid-dirty-row");
59727                     }
59728                     rp.cells = lcb;
59729                     if(this.getRowClass){
59730                         alt.push( this.getRowClass(r, rowIndex));
59731                     }
59732                     if (hasListener) {
59733                         rowcfg = {
59734                              
59735                             record: r,
59736                             rowIndex : rowIndex,
59737                             rowClass : ''
59738                         };
59739                         this.grid.fireEvent('rowclass', this, rowcfg);
59740                         alt.push(rowcfg.rowClass);
59741                     }
59742                     
59743                     rp.alt = alt.join(" ");
59744                     rp.cells = lcb.join("");
59745                     lbuf[lbuf.length] = rt.apply(rp);
59746                     rp.cells = cb.join("");
59747                     buf[buf.length] =  rt.apply(rp);
59748                 }
59749                 return [lbuf.join(""), buf.join("")];
59750             },
59751
59752     renderBody : function(){
59753         var markup = this.renderRows();
59754         var bt = this.templates.body;
59755         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59756     },
59757
59758     /**
59759      * Refreshes the grid
59760      * @param {Boolean} headersToo
59761      */
59762     refresh : function(headersToo){
59763         this.fireEvent("beforerefresh", this);
59764         this.grid.stopEditing();
59765         var result = this.renderBody();
59766         this.lockedBody.update(result[0]);
59767         this.mainBody.update(result[1]);
59768         if(headersToo === true){
59769             this.updateHeaders();
59770             this.updateColumns();
59771             this.updateSplitters();
59772             this.updateHeaderSortState();
59773         }
59774         this.syncRowHeights();
59775         this.layout();
59776         this.fireEvent("refresh", this);
59777     },
59778
59779     handleColumnMove : function(cm, oldIndex, newIndex){
59780         this.indexMap = null;
59781         var s = this.getScrollState();
59782         this.refresh(true);
59783         this.restoreScroll(s);
59784         this.afterMove(newIndex);
59785     },
59786
59787     afterMove : function(colIndex){
59788         if(this.enableMoveAnim && Roo.enableFx){
59789             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59790         }
59791         // if multisort - fix sortOrder, and reload..
59792         if (this.grid.dataSource.multiSort) {
59793             // the we can call sort again..
59794             var dm = this.grid.dataSource;
59795             var cm = this.grid.colModel;
59796             var so = [];
59797             for(var i = 0; i < cm.config.length; i++ ) {
59798                 
59799                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59800                     continue; // dont' bother, it's not in sort list or being set.
59801                 }
59802                 
59803                 so.push(cm.config[i].dataIndex);
59804             };
59805             dm.sortOrder = so;
59806             dm.load(dm.lastOptions);
59807             
59808             
59809         }
59810         
59811     },
59812
59813     updateCell : function(dm, rowIndex, dataIndex){
59814         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59815         if(typeof colIndex == "undefined"){ // not present in grid
59816             return;
59817         }
59818         var cm = this.grid.colModel;
59819         var cell = this.getCell(rowIndex, colIndex);
59820         var cellText = this.getCellText(rowIndex, colIndex);
59821
59822         var p = {
59823             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59824             id : cm.getColumnId(colIndex),
59825             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59826         };
59827         var renderer = cm.getRenderer(colIndex);
59828         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59829         if(typeof val == "undefined" || val === "") {
59830             val = "&#160;";
59831         }
59832         cellText.innerHTML = val;
59833         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59834         this.syncRowHeights(rowIndex, rowIndex);
59835     },
59836
59837     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59838         var maxWidth = 0;
59839         if(this.grid.autoSizeHeaders){
59840             var h = this.getHeaderCellMeasure(colIndex);
59841             maxWidth = Math.max(maxWidth, h.scrollWidth);
59842         }
59843         var tb, index;
59844         if(this.cm.isLocked(colIndex)){
59845             tb = this.getLockedTable();
59846             index = colIndex;
59847         }else{
59848             tb = this.getBodyTable();
59849             index = colIndex - this.cm.getLockedCount();
59850         }
59851         if(tb && tb.rows){
59852             var rows = tb.rows;
59853             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59854             for(var i = 0; i < stopIndex; i++){
59855                 var cell = rows[i].childNodes[index].firstChild;
59856                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59857             }
59858         }
59859         return maxWidth + /*margin for error in IE*/ 5;
59860     },
59861     /**
59862      * Autofit a column to its content.
59863      * @param {Number} colIndex
59864      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59865      */
59866      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59867          if(this.cm.isHidden(colIndex)){
59868              return; // can't calc a hidden column
59869          }
59870         if(forceMinSize){
59871             var cid = this.cm.getColumnId(colIndex);
59872             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59873            if(this.grid.autoSizeHeaders){
59874                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59875            }
59876         }
59877         var newWidth = this.calcColumnWidth(colIndex);
59878         this.cm.setColumnWidth(colIndex,
59879             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59880         if(!suppressEvent){
59881             this.grid.fireEvent("columnresize", colIndex, newWidth);
59882         }
59883     },
59884
59885     /**
59886      * Autofits all columns to their content and then expands to fit any extra space in the grid
59887      */
59888      autoSizeColumns : function(){
59889         var cm = this.grid.colModel;
59890         var colCount = cm.getColumnCount();
59891         for(var i = 0; i < colCount; i++){
59892             this.autoSizeColumn(i, true, true);
59893         }
59894         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59895             this.fitColumns();
59896         }else{
59897             this.updateColumns();
59898             this.layout();
59899         }
59900     },
59901
59902     /**
59903      * Autofits all columns to the grid's width proportionate with their current size
59904      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59905      */
59906     fitColumns : function(reserveScrollSpace){
59907         var cm = this.grid.colModel;
59908         var colCount = cm.getColumnCount();
59909         var cols = [];
59910         var width = 0;
59911         var i, w;
59912         for (i = 0; i < colCount; i++){
59913             if(!cm.isHidden(i) && !cm.isFixed(i)){
59914                 w = cm.getColumnWidth(i);
59915                 cols.push(i);
59916                 cols.push(w);
59917                 width += w;
59918             }
59919         }
59920         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59921         if(reserveScrollSpace){
59922             avail -= 17;
59923         }
59924         var frac = (avail - cm.getTotalWidth())/width;
59925         while (cols.length){
59926             w = cols.pop();
59927             i = cols.pop();
59928             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59929         }
59930         this.updateColumns();
59931         this.layout();
59932     },
59933
59934     onRowSelect : function(rowIndex){
59935         var row = this.getRowComposite(rowIndex);
59936         row.addClass("x-grid-row-selected");
59937     },
59938
59939     onRowDeselect : function(rowIndex){
59940         var row = this.getRowComposite(rowIndex);
59941         row.removeClass("x-grid-row-selected");
59942     },
59943
59944     onCellSelect : function(row, col){
59945         var cell = this.getCell(row, col);
59946         if(cell){
59947             Roo.fly(cell).addClass("x-grid-cell-selected");
59948         }
59949     },
59950
59951     onCellDeselect : function(row, col){
59952         var cell = this.getCell(row, col);
59953         if(cell){
59954             Roo.fly(cell).removeClass("x-grid-cell-selected");
59955         }
59956     },
59957
59958     updateHeaderSortState : function(){
59959         
59960         // sort state can be single { field: xxx, direction : yyy}
59961         // or   { xxx=>ASC , yyy : DESC ..... }
59962         
59963         var mstate = {};
59964         if (!this.ds.multiSort) { 
59965             var state = this.ds.getSortState();
59966             if(!state){
59967                 return;
59968             }
59969             mstate[state.field] = state.direction;
59970             // FIXME... - this is not used here.. but might be elsewhere..
59971             this.sortState = state;
59972             
59973         } else {
59974             mstate = this.ds.sortToggle;
59975         }
59976         //remove existing sort classes..
59977         
59978         var sc = this.sortClasses;
59979         var hds = this.el.select(this.headerSelector).removeClass(sc);
59980         
59981         for(var f in mstate) {
59982         
59983             var sortColumn = this.cm.findColumnIndex(f);
59984             
59985             if(sortColumn != -1){
59986                 var sortDir = mstate[f];        
59987                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59988             }
59989         }
59990         
59991          
59992         
59993     },
59994
59995
59996     handleHeaderClick : function(g, index,e){
59997         
59998         Roo.log("header click");
59999         
60000         if (Roo.isTouch) {
60001             // touch events on header are handled by context
60002             this.handleHdCtx(g,index,e);
60003             return;
60004         }
60005         
60006         
60007         if(this.headersDisabled){
60008             return;
60009         }
60010         var dm = g.dataSource, cm = g.colModel;
60011         if(!cm.isSortable(index)){
60012             return;
60013         }
60014         g.stopEditing();
60015         
60016         if (dm.multiSort) {
60017             // update the sortOrder
60018             var so = [];
60019             for(var i = 0; i < cm.config.length; i++ ) {
60020                 
60021                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60022                     continue; // dont' bother, it's not in sort list or being set.
60023                 }
60024                 
60025                 so.push(cm.config[i].dataIndex);
60026             };
60027             dm.sortOrder = so;
60028         }
60029         
60030         
60031         dm.sort(cm.getDataIndex(index));
60032     },
60033
60034
60035     destroy : function(){
60036         if(this.colMenu){
60037             this.colMenu.removeAll();
60038             Roo.menu.MenuMgr.unregister(this.colMenu);
60039             this.colMenu.getEl().remove();
60040             delete this.colMenu;
60041         }
60042         if(this.hmenu){
60043             this.hmenu.removeAll();
60044             Roo.menu.MenuMgr.unregister(this.hmenu);
60045             this.hmenu.getEl().remove();
60046             delete this.hmenu;
60047         }
60048         if(this.grid.enableColumnMove){
60049             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60050             if(dds){
60051                 for(var dd in dds){
60052                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60053                         var elid = dds[dd].dragElId;
60054                         dds[dd].unreg();
60055                         Roo.get(elid).remove();
60056                     } else if(dds[dd].config.isTarget){
60057                         dds[dd].proxyTop.remove();
60058                         dds[dd].proxyBottom.remove();
60059                         dds[dd].unreg();
60060                     }
60061                     if(Roo.dd.DDM.locationCache[dd]){
60062                         delete Roo.dd.DDM.locationCache[dd];
60063                     }
60064                 }
60065                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60066             }
60067         }
60068         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60069         this.bind(null, null);
60070         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60071     },
60072
60073     handleLockChange : function(){
60074         this.refresh(true);
60075     },
60076
60077     onDenyColumnLock : function(){
60078
60079     },
60080
60081     onDenyColumnHide : function(){
60082
60083     },
60084
60085     handleHdMenuClick : function(item){
60086         var index = this.hdCtxIndex;
60087         var cm = this.cm, ds = this.ds;
60088         switch(item.id){
60089             case "asc":
60090                 ds.sort(cm.getDataIndex(index), "ASC");
60091                 break;
60092             case "desc":
60093                 ds.sort(cm.getDataIndex(index), "DESC");
60094                 break;
60095             case "lock":
60096                 var lc = cm.getLockedCount();
60097                 if(cm.getColumnCount(true) <= lc+1){
60098                     this.onDenyColumnLock();
60099                     return;
60100                 }
60101                 if(lc != index){
60102                     cm.setLocked(index, true, true);
60103                     cm.moveColumn(index, lc);
60104                     this.grid.fireEvent("columnmove", index, lc);
60105                 }else{
60106                     cm.setLocked(index, true);
60107                 }
60108             break;
60109             case "unlock":
60110                 var lc = cm.getLockedCount();
60111                 if((lc-1) != index){
60112                     cm.setLocked(index, false, true);
60113                     cm.moveColumn(index, lc-1);
60114                     this.grid.fireEvent("columnmove", index, lc-1);
60115                 }else{
60116                     cm.setLocked(index, false);
60117                 }
60118             break;
60119             case 'wider': // used to expand cols on touch..
60120             case 'narrow':
60121                 var cw = cm.getColumnWidth(index);
60122                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60123                 cw = Math.max(0, cw);
60124                 cw = Math.min(cw,4000);
60125                 cm.setColumnWidth(index, cw);
60126                 break;
60127                 
60128             default:
60129                 index = cm.getIndexById(item.id.substr(4));
60130                 if(index != -1){
60131                     if(item.checked && cm.getColumnCount(true) <= 1){
60132                         this.onDenyColumnHide();
60133                         return false;
60134                     }
60135                     cm.setHidden(index, item.checked);
60136                 }
60137         }
60138         return true;
60139     },
60140
60141     beforeColMenuShow : function(){
60142         var cm = this.cm,  colCount = cm.getColumnCount();
60143         this.colMenu.removeAll();
60144         for(var i = 0; i < colCount; i++){
60145             this.colMenu.add(new Roo.menu.CheckItem({
60146                 id: "col-"+cm.getColumnId(i),
60147                 text: cm.getColumnHeader(i),
60148                 checked: !cm.isHidden(i),
60149                 hideOnClick:false
60150             }));
60151         }
60152     },
60153
60154     handleHdCtx : function(g, index, e){
60155         e.stopEvent();
60156         var hd = this.getHeaderCell(index);
60157         this.hdCtxIndex = index;
60158         var ms = this.hmenu.items, cm = this.cm;
60159         ms.get("asc").setDisabled(!cm.isSortable(index));
60160         ms.get("desc").setDisabled(!cm.isSortable(index));
60161         if(this.grid.enableColLock !== false){
60162             ms.get("lock").setDisabled(cm.isLocked(index));
60163             ms.get("unlock").setDisabled(!cm.isLocked(index));
60164         }
60165         this.hmenu.show(hd, "tl-bl");
60166     },
60167
60168     handleHdOver : function(e){
60169         var hd = this.findHeaderCell(e.getTarget());
60170         if(hd && !this.headersDisabled){
60171             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60172                this.fly(hd).addClass("x-grid-hd-over");
60173             }
60174         }
60175     },
60176
60177     handleHdOut : function(e){
60178         var hd = this.findHeaderCell(e.getTarget());
60179         if(hd){
60180             this.fly(hd).removeClass("x-grid-hd-over");
60181         }
60182     },
60183
60184     handleSplitDblClick : function(e, t){
60185         var i = this.getCellIndex(t);
60186         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60187             this.autoSizeColumn(i, true);
60188             this.layout();
60189         }
60190     },
60191
60192     render : function(){
60193
60194         var cm = this.cm;
60195         var colCount = cm.getColumnCount();
60196
60197         if(this.grid.monitorWindowResize === true){
60198             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60199         }
60200         var header = this.renderHeaders();
60201         var body = this.templates.body.apply({rows:""});
60202         var html = this.templates.master.apply({
60203             lockedBody: body,
60204             body: body,
60205             lockedHeader: header[0],
60206             header: header[1]
60207         });
60208
60209         //this.updateColumns();
60210
60211         this.grid.getGridEl().dom.innerHTML = html;
60212
60213         this.initElements();
60214         
60215         // a kludge to fix the random scolling effect in webkit
60216         this.el.on("scroll", function() {
60217             this.el.dom.scrollTop=0; // hopefully not recursive..
60218         },this);
60219
60220         this.scroller.on("scroll", this.handleScroll, this);
60221         this.lockedBody.on("mousewheel", this.handleWheel, this);
60222         this.mainBody.on("mousewheel", this.handleWheel, this);
60223
60224         this.mainHd.on("mouseover", this.handleHdOver, this);
60225         this.mainHd.on("mouseout", this.handleHdOut, this);
60226         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60227                 {delegate: "."+this.splitClass});
60228
60229         this.lockedHd.on("mouseover", this.handleHdOver, this);
60230         this.lockedHd.on("mouseout", this.handleHdOut, this);
60231         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60232                 {delegate: "."+this.splitClass});
60233
60234         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60235             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60236         }
60237
60238         this.updateSplitters();
60239
60240         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60241             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60242             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60243         }
60244
60245         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60246             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60247             this.hmenu.add(
60248                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60249                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60250             );
60251             if(this.grid.enableColLock !== false){
60252                 this.hmenu.add('-',
60253                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60254                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60255                 );
60256             }
60257             if (Roo.isTouch) {
60258                  this.hmenu.add('-',
60259                     {id:"wider", text: this.columnsWiderText},
60260                     {id:"narrow", text: this.columnsNarrowText }
60261                 );
60262                 
60263                  
60264             }
60265             
60266             if(this.grid.enableColumnHide !== false){
60267
60268                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60269                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60270                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60271
60272                 this.hmenu.add('-',
60273                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60274                 );
60275             }
60276             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60277
60278             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60279         }
60280
60281         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60282             this.dd = new Roo.grid.GridDragZone(this.grid, {
60283                 ddGroup : this.grid.ddGroup || 'GridDD'
60284             });
60285             
60286         }
60287
60288         /*
60289         for(var i = 0; i < colCount; i++){
60290             if(cm.isHidden(i)){
60291                 this.hideColumn(i);
60292             }
60293             if(cm.config[i].align){
60294                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60295                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60296             }
60297         }*/
60298         
60299         this.updateHeaderSortState();
60300
60301         this.beforeInitialResize();
60302         this.layout(true);
60303
60304         // two part rendering gives faster view to the user
60305         this.renderPhase2.defer(1, this);
60306     },
60307
60308     renderPhase2 : function(){
60309         // render the rows now
60310         this.refresh();
60311         if(this.grid.autoSizeColumns){
60312             this.autoSizeColumns();
60313         }
60314     },
60315
60316     beforeInitialResize : function(){
60317
60318     },
60319
60320     onColumnSplitterMoved : function(i, w){
60321         this.userResized = true;
60322         var cm = this.grid.colModel;
60323         cm.setColumnWidth(i, w, true);
60324         var cid = cm.getColumnId(i);
60325         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60326         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60327         this.updateSplitters();
60328         this.layout();
60329         this.grid.fireEvent("columnresize", i, w);
60330     },
60331
60332     syncRowHeights : function(startIndex, endIndex){
60333         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60334             startIndex = startIndex || 0;
60335             var mrows = this.getBodyTable().rows;
60336             var lrows = this.getLockedTable().rows;
60337             var len = mrows.length-1;
60338             endIndex = Math.min(endIndex || len, len);
60339             for(var i = startIndex; i <= endIndex; i++){
60340                 var m = mrows[i], l = lrows[i];
60341                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60342                 m.style.height = l.style.height = h + "px";
60343             }
60344         }
60345     },
60346
60347     layout : function(initialRender, is2ndPass)
60348     {
60349         var g = this.grid;
60350         var auto = g.autoHeight;
60351         var scrollOffset = 16;
60352         var c = g.getGridEl(), cm = this.cm,
60353                 expandCol = g.autoExpandColumn,
60354                 gv = this;
60355         //c.beginMeasure();
60356
60357         if(!c.dom.offsetWidth){ // display:none?
60358             if(initialRender){
60359                 this.lockedWrap.show();
60360                 this.mainWrap.show();
60361             }
60362             return;
60363         }
60364
60365         var hasLock = this.cm.isLocked(0);
60366
60367         var tbh = this.headerPanel.getHeight();
60368         var bbh = this.footerPanel.getHeight();
60369
60370         if(auto){
60371             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60372             var newHeight = ch + c.getBorderWidth("tb");
60373             if(g.maxHeight){
60374                 newHeight = Math.min(g.maxHeight, newHeight);
60375             }
60376             c.setHeight(newHeight);
60377         }
60378
60379         if(g.autoWidth){
60380             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60381         }
60382
60383         var s = this.scroller;
60384
60385         var csize = c.getSize(true);
60386
60387         this.el.setSize(csize.width, csize.height);
60388
60389         this.headerPanel.setWidth(csize.width);
60390         this.footerPanel.setWidth(csize.width);
60391
60392         var hdHeight = this.mainHd.getHeight();
60393         var vw = csize.width;
60394         var vh = csize.height - (tbh + bbh);
60395
60396         s.setSize(vw, vh);
60397
60398         var bt = this.getBodyTable();
60399         
60400         if(cm.getLockedCount() == cm.config.length){
60401             bt = this.getLockedTable();
60402         }
60403         
60404         var ltWidth = hasLock ?
60405                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60406
60407         var scrollHeight = bt.offsetHeight;
60408         var scrollWidth = ltWidth + bt.offsetWidth;
60409         var vscroll = false, hscroll = false;
60410
60411         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60412
60413         var lw = this.lockedWrap, mw = this.mainWrap;
60414         var lb = this.lockedBody, mb = this.mainBody;
60415
60416         setTimeout(function(){
60417             var t = s.dom.offsetTop;
60418             var w = s.dom.clientWidth,
60419                 h = s.dom.clientHeight;
60420
60421             lw.setTop(t);
60422             lw.setSize(ltWidth, h);
60423
60424             mw.setLeftTop(ltWidth, t);
60425             mw.setSize(w-ltWidth, h);
60426
60427             lb.setHeight(h-hdHeight);
60428             mb.setHeight(h-hdHeight);
60429
60430             if(is2ndPass !== true && !gv.userResized && expandCol){
60431                 // high speed resize without full column calculation
60432                 
60433                 var ci = cm.getIndexById(expandCol);
60434                 if (ci < 0) {
60435                     ci = cm.findColumnIndex(expandCol);
60436                 }
60437                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60438                 var expandId = cm.getColumnId(ci);
60439                 var  tw = cm.getTotalWidth(false);
60440                 var currentWidth = cm.getColumnWidth(ci);
60441                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60442                 if(currentWidth != cw){
60443                     cm.setColumnWidth(ci, cw, true);
60444                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60445                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60446                     gv.updateSplitters();
60447                     gv.layout(false, true);
60448                 }
60449             }
60450
60451             if(initialRender){
60452                 lw.show();
60453                 mw.show();
60454             }
60455             //c.endMeasure();
60456         }, 10);
60457     },
60458
60459     onWindowResize : function(){
60460         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60461             return;
60462         }
60463         this.layout();
60464     },
60465
60466     appendFooter : function(parentEl){
60467         return null;
60468     },
60469
60470     sortAscText : "Sort Ascending",
60471     sortDescText : "Sort Descending",
60472     lockText : "Lock Column",
60473     unlockText : "Unlock Column",
60474     columnsText : "Columns",
60475  
60476     columnsWiderText : "Wider",
60477     columnsNarrowText : "Thinner"
60478 });
60479
60480
60481 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60482     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60483     this.proxy.el.addClass('x-grid3-col-dd');
60484 };
60485
60486 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60487     handleMouseDown : function(e){
60488
60489     },
60490
60491     callHandleMouseDown : function(e){
60492         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60493     }
60494 });
60495 /*
60496  * Based on:
60497  * Ext JS Library 1.1.1
60498  * Copyright(c) 2006-2007, Ext JS, LLC.
60499  *
60500  * Originally Released Under LGPL - original licence link has changed is not relivant.
60501  *
60502  * Fork - LGPL
60503  * <script type="text/javascript">
60504  */
60505  /**
60506  * @extends Roo.dd.DDProxy
60507  * @class Roo.grid.SplitDragZone
60508  * Support for Column Header resizing
60509  * @constructor
60510  * @param {Object} config
60511  */
60512 // private
60513 // This is a support class used internally by the Grid components
60514 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60515     this.grid = grid;
60516     this.view = grid.getView();
60517     this.proxy = this.view.resizeProxy;
60518     Roo.grid.SplitDragZone.superclass.constructor.call(
60519         this,
60520         hd, // ID
60521         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60522         {  // CONFIG
60523             dragElId : Roo.id(this.proxy.dom),
60524             resizeFrame:false
60525         }
60526     );
60527     
60528     this.setHandleElId(Roo.id(hd));
60529     if (hd2 !== false) {
60530         this.setOuterHandleElId(Roo.id(hd2));
60531     }
60532     
60533     this.scroll = false;
60534 };
60535 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60536     fly: Roo.Element.fly,
60537
60538     b4StartDrag : function(x, y){
60539         this.view.headersDisabled = true;
60540         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60541                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60542         );
60543         this.proxy.setHeight(h);
60544         
60545         // for old system colWidth really stored the actual width?
60546         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60547         // which in reality did not work.. - it worked only for fixed sizes
60548         // for resizable we need to use actual sizes.
60549         var w = this.cm.getColumnWidth(this.cellIndex);
60550         if (!this.view.mainWrap) {
60551             // bootstrap.
60552             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60553         }
60554         
60555         
60556         
60557         // this was w-this.grid.minColumnWidth;
60558         // doesnt really make sense? - w = thie curren width or the rendered one?
60559         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60560         this.resetConstraints();
60561         this.setXConstraint(minw, 1000);
60562         this.setYConstraint(0, 0);
60563         this.minX = x - minw;
60564         this.maxX = x + 1000;
60565         this.startPos = x;
60566         if (!this.view.mainWrap) { // this is Bootstrap code..
60567             this.getDragEl().style.display='block';
60568         }
60569         
60570         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60571     },
60572
60573
60574     handleMouseDown : function(e){
60575         ev = Roo.EventObject.setEvent(e);
60576         var t = this.fly(ev.getTarget());
60577         if(t.hasClass("x-grid-split")){
60578             this.cellIndex = this.view.getCellIndex(t.dom);
60579             this.split = t.dom;
60580             this.cm = this.grid.colModel;
60581             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60582                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60583             }
60584         }
60585     },
60586
60587     endDrag : function(e){
60588         this.view.headersDisabled = false;
60589         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60590         var diff = endX - this.startPos;
60591         // 
60592         var w = this.cm.getColumnWidth(this.cellIndex);
60593         if (!this.view.mainWrap) {
60594             w = 0;
60595         }
60596         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60597     },
60598
60599     autoOffset : function(){
60600         this.setDelta(0,0);
60601     }
60602 });/*
60603  * Based on:
60604  * Ext JS Library 1.1.1
60605  * Copyright(c) 2006-2007, Ext JS, LLC.
60606  *
60607  * Originally Released Under LGPL - original licence link has changed is not relivant.
60608  *
60609  * Fork - LGPL
60610  * <script type="text/javascript">
60611  */
60612  
60613 // private
60614 // This is a support class used internally by the Grid components
60615 Roo.grid.GridDragZone = function(grid, config){
60616     this.view = grid.getView();
60617     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60618     if(this.view.lockedBody){
60619         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60620         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60621     }
60622     this.scroll = false;
60623     this.grid = grid;
60624     this.ddel = document.createElement('div');
60625     this.ddel.className = 'x-grid-dd-wrap';
60626 };
60627
60628 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60629     ddGroup : "GridDD",
60630
60631     getDragData : function(e){
60632         var t = Roo.lib.Event.getTarget(e);
60633         var rowIndex = this.view.findRowIndex(t);
60634         var sm = this.grid.selModel;
60635             
60636         //Roo.log(rowIndex);
60637         
60638         if (sm.getSelectedCell) {
60639             // cell selection..
60640             if (!sm.getSelectedCell()) {
60641                 return false;
60642             }
60643             if (rowIndex != sm.getSelectedCell()[0]) {
60644                 return false;
60645             }
60646         
60647         }
60648         if (sm.getSelections && sm.getSelections().length < 1) {
60649             return false;
60650         }
60651         
60652         
60653         // before it used to all dragging of unseleted... - now we dont do that.
60654         if(rowIndex !== false){
60655             
60656             // if editorgrid.. 
60657             
60658             
60659             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60660                
60661             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60662               //  
60663             //}
60664             if (e.hasModifier()){
60665                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60666             }
60667             
60668             Roo.log("getDragData");
60669             
60670             return {
60671                 grid: this.grid,
60672                 ddel: this.ddel,
60673                 rowIndex: rowIndex,
60674                 selections: sm.getSelections ? sm.getSelections() : (
60675                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60676             };
60677         }
60678         return false;
60679     },
60680     
60681     
60682     onInitDrag : function(e){
60683         var data = this.dragData;
60684         this.ddel.innerHTML = this.grid.getDragDropText();
60685         this.proxy.update(this.ddel);
60686         // fire start drag?
60687     },
60688
60689     afterRepair : function(){
60690         this.dragging = false;
60691     },
60692
60693     getRepairXY : function(e, data){
60694         return false;
60695     },
60696
60697     onEndDrag : function(data, e){
60698         // fire end drag?
60699     },
60700
60701     onValidDrop : function(dd, e, id){
60702         // fire drag drop?
60703         this.hideProxy();
60704     },
60705
60706     beforeInvalidDrop : function(e, id){
60707
60708     }
60709 });/*
60710  * Based on:
60711  * Ext JS Library 1.1.1
60712  * Copyright(c) 2006-2007, Ext JS, LLC.
60713  *
60714  * Originally Released Under LGPL - original licence link has changed is not relivant.
60715  *
60716  * Fork - LGPL
60717  * <script type="text/javascript">
60718  */
60719  
60720
60721 /**
60722  * @class Roo.grid.ColumnModel
60723  * @extends Roo.util.Observable
60724  * This is the default implementation of a ColumnModel used by the Grid. It defines
60725  * the columns in the grid.
60726  * <br>Usage:<br>
60727  <pre><code>
60728  var colModel = new Roo.grid.ColumnModel([
60729         {header: "Ticker", width: 60, sortable: true, locked: true},
60730         {header: "Company Name", width: 150, sortable: true},
60731         {header: "Market Cap.", width: 100, sortable: true},
60732         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60733         {header: "Employees", width: 100, sortable: true, resizable: false}
60734  ]);
60735  </code></pre>
60736  * <p>
60737  
60738  * The config options listed for this class are options which may appear in each
60739  * individual column definition.
60740  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60741  * @constructor
60742  * @param {Object} config An Array of column config objects. See this class's
60743  * config objects for details.
60744 */
60745 Roo.grid.ColumnModel = function(config){
60746         /**
60747      * The config passed into the constructor
60748      */
60749     this.config = []; //config;
60750     this.lookup = {};
60751
60752     // if no id, create one
60753     // if the column does not have a dataIndex mapping,
60754     // map it to the order it is in the config
60755     for(var i = 0, len = config.length; i < len; i++){
60756         this.addColumn(config[i]);
60757         
60758     }
60759
60760     /**
60761      * The width of columns which have no width specified (defaults to 100)
60762      * @type Number
60763      */
60764     this.defaultWidth = 100;
60765
60766     /**
60767      * Default sortable of columns which have no sortable specified (defaults to false)
60768      * @type Boolean
60769      */
60770     this.defaultSortable = false;
60771
60772     this.addEvents({
60773         /**
60774              * @event widthchange
60775              * Fires when the width of a column changes.
60776              * @param {ColumnModel} this
60777              * @param {Number} columnIndex The column index
60778              * @param {Number} newWidth The new width
60779              */
60780             "widthchange": true,
60781         /**
60782              * @event headerchange
60783              * Fires when the text of a header changes.
60784              * @param {ColumnModel} this
60785              * @param {Number} columnIndex The column index
60786              * @param {Number} newText The new header text
60787              */
60788             "headerchange": true,
60789         /**
60790              * @event hiddenchange
60791              * Fires when a column is hidden or "unhidden".
60792              * @param {ColumnModel} this
60793              * @param {Number} columnIndex The column index
60794              * @param {Boolean} hidden true if hidden, false otherwise
60795              */
60796             "hiddenchange": true,
60797             /**
60798          * @event columnmoved
60799          * Fires when a column is moved.
60800          * @param {ColumnModel} this
60801          * @param {Number} oldIndex
60802          * @param {Number} newIndex
60803          */
60804         "columnmoved" : true,
60805         /**
60806          * @event columlockchange
60807          * Fires when a column's locked state is changed
60808          * @param {ColumnModel} this
60809          * @param {Number} colIndex
60810          * @param {Boolean} locked true if locked
60811          */
60812         "columnlockchange" : true
60813     });
60814     Roo.grid.ColumnModel.superclass.constructor.call(this);
60815 };
60816 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60817     /**
60818      * @cfg {String} header The header text to display in the Grid view.
60819      */
60820         /**
60821      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60822      */
60823         /**
60824      * @cfg {String} smHeader Header at Bootsrap Small width
60825      */
60826         /**
60827      * @cfg {String} mdHeader Header at Bootsrap Medium width
60828      */
60829         /**
60830      * @cfg {String} lgHeader Header at Bootsrap Large width
60831      */
60832         /**
60833      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60834      */
60835     /**
60836      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60837      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60838      * specified, the column's index is used as an index into the Record's data Array.
60839      */
60840     /**
60841      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60842      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60843      */
60844     /**
60845      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60846      * Defaults to the value of the {@link #defaultSortable} property.
60847      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60848      */
60849     /**
60850      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60851      */
60852     /**
60853      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60854      */
60855     /**
60856      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60857      */
60858     /**
60859      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60860      */
60861     /**
60862      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60863      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60864      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60865      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60866      */
60867        /**
60868      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60869      */
60870     /**
60871      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60872      */
60873     /**
60874      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60875      */
60876     /**
60877      * @cfg {String} cursor (Optional)
60878      */
60879     /**
60880      * @cfg {String} tooltip (Optional)
60881      */
60882     /**
60883      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60884      */
60885     /**
60886      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60887      */
60888     /**
60889      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60890      */
60891     /**
60892      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60893      */
60894         /**
60895      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60896      */
60897     /**
60898      * Returns the id of the column at the specified index.
60899      * @param {Number} index The column index
60900      * @return {String} the id
60901      */
60902     getColumnId : function(index){
60903         return this.config[index].id;
60904     },
60905
60906     /**
60907      * Returns the column for a specified id.
60908      * @param {String} id The column id
60909      * @return {Object} the column
60910      */
60911     getColumnById : function(id){
60912         return this.lookup[id];
60913     },
60914
60915     
60916     /**
60917      * Returns the column Object for a specified dataIndex.
60918      * @param {String} dataIndex The column dataIndex
60919      * @return {Object|Boolean} the column or false if not found
60920      */
60921     getColumnByDataIndex: function(dataIndex){
60922         var index = this.findColumnIndex(dataIndex);
60923         return index > -1 ? this.config[index] : false;
60924     },
60925     
60926     /**
60927      * Returns the index for a specified column id.
60928      * @param {String} id The column id
60929      * @return {Number} the index, or -1 if not found
60930      */
60931     getIndexById : function(id){
60932         for(var i = 0, len = this.config.length; i < len; i++){
60933             if(this.config[i].id == id){
60934                 return i;
60935             }
60936         }
60937         return -1;
60938     },
60939     
60940     /**
60941      * Returns the index for a specified column dataIndex.
60942      * @param {String} dataIndex The column dataIndex
60943      * @return {Number} the index, or -1 if not found
60944      */
60945     
60946     findColumnIndex : function(dataIndex){
60947         for(var i = 0, len = this.config.length; i < len; i++){
60948             if(this.config[i].dataIndex == dataIndex){
60949                 return i;
60950             }
60951         }
60952         return -1;
60953     },
60954     
60955     
60956     moveColumn : function(oldIndex, newIndex){
60957         var c = this.config[oldIndex];
60958         this.config.splice(oldIndex, 1);
60959         this.config.splice(newIndex, 0, c);
60960         this.dataMap = null;
60961         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60962     },
60963
60964     isLocked : function(colIndex){
60965         return this.config[colIndex].locked === true;
60966     },
60967
60968     setLocked : function(colIndex, value, suppressEvent){
60969         if(this.isLocked(colIndex) == value){
60970             return;
60971         }
60972         this.config[colIndex].locked = value;
60973         if(!suppressEvent){
60974             this.fireEvent("columnlockchange", this, colIndex, value);
60975         }
60976     },
60977
60978     getTotalLockedWidth : function(){
60979         var totalWidth = 0;
60980         for(var i = 0; i < this.config.length; i++){
60981             if(this.isLocked(i) && !this.isHidden(i)){
60982                 this.totalWidth += this.getColumnWidth(i);
60983             }
60984         }
60985         return totalWidth;
60986     },
60987
60988     getLockedCount : function(){
60989         for(var i = 0, len = this.config.length; i < len; i++){
60990             if(!this.isLocked(i)){
60991                 return i;
60992             }
60993         }
60994         
60995         return this.config.length;
60996     },
60997
60998     /**
60999      * Returns the number of columns.
61000      * @return {Number}
61001      */
61002     getColumnCount : function(visibleOnly){
61003         if(visibleOnly === true){
61004             var c = 0;
61005             for(var i = 0, len = this.config.length; i < len; i++){
61006                 if(!this.isHidden(i)){
61007                     c++;
61008                 }
61009             }
61010             return c;
61011         }
61012         return this.config.length;
61013     },
61014
61015     /**
61016      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61017      * @param {Function} fn
61018      * @param {Object} scope (optional)
61019      * @return {Array} result
61020      */
61021     getColumnsBy : function(fn, scope){
61022         var r = [];
61023         for(var i = 0, len = this.config.length; i < len; i++){
61024             var c = this.config[i];
61025             if(fn.call(scope||this, c, i) === true){
61026                 r[r.length] = c;
61027             }
61028         }
61029         return r;
61030     },
61031
61032     /**
61033      * Returns true if the specified column is sortable.
61034      * @param {Number} col The column index
61035      * @return {Boolean}
61036      */
61037     isSortable : function(col){
61038         if(typeof this.config[col].sortable == "undefined"){
61039             return this.defaultSortable;
61040         }
61041         return this.config[col].sortable;
61042     },
61043
61044     /**
61045      * Returns the rendering (formatting) function defined for the column.
61046      * @param {Number} col The column index.
61047      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61048      */
61049     getRenderer : function(col){
61050         if(!this.config[col].renderer){
61051             return Roo.grid.ColumnModel.defaultRenderer;
61052         }
61053         return this.config[col].renderer;
61054     },
61055
61056     /**
61057      * Sets the rendering (formatting) function for a column.
61058      * @param {Number} col The column index
61059      * @param {Function} fn The function to use to process the cell's raw data
61060      * to return HTML markup for the grid view. The render function is called with
61061      * the following parameters:<ul>
61062      * <li>Data value.</li>
61063      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61064      * <li>css A CSS style string to apply to the table cell.</li>
61065      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61066      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61067      * <li>Row index</li>
61068      * <li>Column index</li>
61069      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61070      */
61071     setRenderer : function(col, fn){
61072         this.config[col].renderer = fn;
61073     },
61074
61075     /**
61076      * Returns the width for the specified column.
61077      * @param {Number} col The column index
61078      * @param (optional) {String} gridSize bootstrap width size.
61079      * @return {Number}
61080      */
61081     getColumnWidth : function(col, gridSize)
61082         {
61083                 var cfg = this.config[col];
61084                 
61085                 if (typeof(gridSize) == 'undefined') {
61086                         return cfg.width * 1 || this.defaultWidth;
61087                 }
61088                 if (gridSize === false) { // if we set it..
61089                         return cfg.width || false;
61090                 }
61091                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61092                 
61093                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61094                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61095                                 continue;
61096                         }
61097                         return cfg[ sizes[i] ];
61098                 }
61099                 return 1;
61100                 
61101     },
61102
61103     /**
61104      * Sets the width for a column.
61105      * @param {Number} col The column index
61106      * @param {Number} width The new width
61107      */
61108     setColumnWidth : function(col, width, suppressEvent){
61109         this.config[col].width = width;
61110         this.totalWidth = null;
61111         if(!suppressEvent){
61112              this.fireEvent("widthchange", this, col, width);
61113         }
61114     },
61115
61116     /**
61117      * Returns the total width of all columns.
61118      * @param {Boolean} includeHidden True to include hidden column widths
61119      * @return {Number}
61120      */
61121     getTotalWidth : function(includeHidden){
61122         if(!this.totalWidth){
61123             this.totalWidth = 0;
61124             for(var i = 0, len = this.config.length; i < len; i++){
61125                 if(includeHidden || !this.isHidden(i)){
61126                     this.totalWidth += this.getColumnWidth(i);
61127                 }
61128             }
61129         }
61130         return this.totalWidth;
61131     },
61132
61133     /**
61134      * Returns the header for the specified column.
61135      * @param {Number} col The column index
61136      * @return {String}
61137      */
61138     getColumnHeader : function(col){
61139         return this.config[col].header;
61140     },
61141
61142     /**
61143      * Sets the header for a column.
61144      * @param {Number} col The column index
61145      * @param {String} header The new header
61146      */
61147     setColumnHeader : function(col, header){
61148         this.config[col].header = header;
61149         this.fireEvent("headerchange", this, col, header);
61150     },
61151
61152     /**
61153      * Returns the tooltip for the specified column.
61154      * @param {Number} col The column index
61155      * @return {String}
61156      */
61157     getColumnTooltip : function(col){
61158             return this.config[col].tooltip;
61159     },
61160     /**
61161      * Sets the tooltip for a column.
61162      * @param {Number} col The column index
61163      * @param {String} tooltip The new tooltip
61164      */
61165     setColumnTooltip : function(col, tooltip){
61166             this.config[col].tooltip = tooltip;
61167     },
61168
61169     /**
61170      * Returns the dataIndex for the specified column.
61171      * @param {Number} col The column index
61172      * @return {Number}
61173      */
61174     getDataIndex : function(col){
61175         return this.config[col].dataIndex;
61176     },
61177
61178     /**
61179      * Sets the dataIndex for a column.
61180      * @param {Number} col The column index
61181      * @param {Number} dataIndex The new dataIndex
61182      */
61183     setDataIndex : function(col, dataIndex){
61184         this.config[col].dataIndex = dataIndex;
61185     },
61186
61187     
61188     
61189     /**
61190      * Returns true if the cell is editable.
61191      * @param {Number} colIndex The column index
61192      * @param {Number} rowIndex The row index - this is nto actually used..?
61193      * @return {Boolean}
61194      */
61195     isCellEditable : function(colIndex, rowIndex){
61196         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61197     },
61198
61199     /**
61200      * Returns the editor defined for the cell/column.
61201      * return false or null to disable editing.
61202      * @param {Number} colIndex The column index
61203      * @param {Number} rowIndex The row index
61204      * @return {Object}
61205      */
61206     getCellEditor : function(colIndex, rowIndex){
61207         return this.config[colIndex].editor;
61208     },
61209
61210     /**
61211      * Sets if a column is editable.
61212      * @param {Number} col The column index
61213      * @param {Boolean} editable True if the column is editable
61214      */
61215     setEditable : function(col, editable){
61216         this.config[col].editable = editable;
61217     },
61218
61219
61220     /**
61221      * Returns true if the column is hidden.
61222      * @param {Number} colIndex The column index
61223      * @return {Boolean}
61224      */
61225     isHidden : function(colIndex){
61226         return this.config[colIndex].hidden;
61227     },
61228
61229
61230     /**
61231      * Returns true if the column width cannot be changed
61232      */
61233     isFixed : function(colIndex){
61234         return this.config[colIndex].fixed;
61235     },
61236
61237     /**
61238      * Returns true if the column can be resized
61239      * @return {Boolean}
61240      */
61241     isResizable : function(colIndex){
61242         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61243     },
61244     /**
61245      * Sets if a column is hidden.
61246      * @param {Number} colIndex The column index
61247      * @param {Boolean} hidden True if the column is hidden
61248      */
61249     setHidden : function(colIndex, hidden){
61250         this.config[colIndex].hidden = hidden;
61251         this.totalWidth = null;
61252         this.fireEvent("hiddenchange", this, colIndex, hidden);
61253     },
61254
61255     /**
61256      * Sets the editor for a column.
61257      * @param {Number} col The column index
61258      * @param {Object} editor The editor object
61259      */
61260     setEditor : function(col, editor){
61261         this.config[col].editor = editor;
61262     },
61263     /**
61264      * Add a column (experimental...) - defaults to adding to the end..
61265      * @param {Object} config 
61266     */
61267     addColumn : function(c)
61268     {
61269     
61270         var i = this.config.length;
61271         this.config[i] = c;
61272         
61273         if(typeof c.dataIndex == "undefined"){
61274             c.dataIndex = i;
61275         }
61276         if(typeof c.renderer == "string"){
61277             c.renderer = Roo.util.Format[c.renderer];
61278         }
61279         if(typeof c.id == "undefined"){
61280             c.id = Roo.id();
61281         }
61282         if(c.editor && c.editor.xtype){
61283             c.editor  = Roo.factory(c.editor, Roo.grid);
61284         }
61285         if(c.editor && c.editor.isFormField){
61286             c.editor = new Roo.grid.GridEditor(c.editor);
61287         }
61288         this.lookup[c.id] = c;
61289     }
61290     
61291 });
61292
61293 Roo.grid.ColumnModel.defaultRenderer = function(value)
61294 {
61295     if(typeof value == "object") {
61296         return value;
61297     }
61298         if(typeof value == "string" && value.length < 1){
61299             return "&#160;";
61300         }
61301     
61302         return String.format("{0}", value);
61303 };
61304
61305 // Alias for backwards compatibility
61306 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61307 /*
61308  * Based on:
61309  * Ext JS Library 1.1.1
61310  * Copyright(c) 2006-2007, Ext JS, LLC.
61311  *
61312  * Originally Released Under LGPL - original licence link has changed is not relivant.
61313  *
61314  * Fork - LGPL
61315  * <script type="text/javascript">
61316  */
61317
61318 /**
61319  * @class Roo.grid.AbstractSelectionModel
61320  * @extends Roo.util.Observable
61321  * @abstract
61322  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61323  * implemented by descendant classes.  This class should not be directly instantiated.
61324  * @constructor
61325  */
61326 Roo.grid.AbstractSelectionModel = function(){
61327     this.locked = false;
61328     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61329 };
61330
61331 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61332     /** @ignore Called by the grid automatically. Do not call directly. */
61333     init : function(grid){
61334         this.grid = grid;
61335         this.initEvents();
61336     },
61337
61338     /**
61339      * Locks the selections.
61340      */
61341     lock : function(){
61342         this.locked = true;
61343     },
61344
61345     /**
61346      * Unlocks the selections.
61347      */
61348     unlock : function(){
61349         this.locked = false;
61350     },
61351
61352     /**
61353      * Returns true if the selections are locked.
61354      * @return {Boolean}
61355      */
61356     isLocked : function(){
61357         return this.locked;
61358     }
61359 });/*
61360  * Based on:
61361  * Ext JS Library 1.1.1
61362  * Copyright(c) 2006-2007, Ext JS, LLC.
61363  *
61364  * Originally Released Under LGPL - original licence link has changed is not relivant.
61365  *
61366  * Fork - LGPL
61367  * <script type="text/javascript">
61368  */
61369 /**
61370  * @extends Roo.grid.AbstractSelectionModel
61371  * @class Roo.grid.RowSelectionModel
61372  * The default SelectionModel used by {@link Roo.grid.Grid}.
61373  * It supports multiple selections and keyboard selection/navigation. 
61374  * @constructor
61375  * @param {Object} config
61376  */
61377 Roo.grid.RowSelectionModel = function(config){
61378     Roo.apply(this, config);
61379     this.selections = new Roo.util.MixedCollection(false, function(o){
61380         return o.id;
61381     });
61382
61383     this.last = false;
61384     this.lastActive = false;
61385
61386     this.addEvents({
61387         /**
61388         * @event selectionchange
61389         * Fires when the selection changes
61390         * @param {SelectionModel} this
61391         */
61392        "selectionchange" : true,
61393        /**
61394         * @event afterselectionchange
61395         * Fires after the selection changes (eg. by key press or clicking)
61396         * @param {SelectionModel} this
61397         */
61398        "afterselectionchange" : true,
61399        /**
61400         * @event beforerowselect
61401         * Fires when a row is selected being selected, return false to cancel.
61402         * @param {SelectionModel} this
61403         * @param {Number} rowIndex The selected index
61404         * @param {Boolean} keepExisting False if other selections will be cleared
61405         */
61406        "beforerowselect" : true,
61407        /**
61408         * @event rowselect
61409         * Fires when a row is selected.
61410         * @param {SelectionModel} this
61411         * @param {Number} rowIndex The selected index
61412         * @param {Roo.data.Record} r The record
61413         */
61414        "rowselect" : true,
61415        /**
61416         * @event rowdeselect
61417         * Fires when a row is deselected.
61418         * @param {SelectionModel} this
61419         * @param {Number} rowIndex The selected index
61420         */
61421         "rowdeselect" : true
61422     });
61423     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61424     this.locked = false;
61425 };
61426
61427 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61428     /**
61429      * @cfg {Boolean} singleSelect
61430      * True to allow selection of only one row at a time (defaults to false)
61431      */
61432     singleSelect : false,
61433
61434     // private
61435     initEvents : function(){
61436
61437         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61438             this.grid.on("mousedown", this.handleMouseDown, this);
61439         }else{ // allow click to work like normal
61440             this.grid.on("rowclick", this.handleDragableRowClick, this);
61441         }
61442         // bootstrap does not have a view..
61443         var view = this.grid.view ? this.grid.view : this.grid;
61444         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61445             "up" : function(e){
61446                 if(!e.shiftKey){
61447                     this.selectPrevious(e.shiftKey);
61448                 }else if(this.last !== false && this.lastActive !== false){
61449                     var last = this.last;
61450                     this.selectRange(this.last,  this.lastActive-1);
61451                     view.focusRow(this.lastActive);
61452                     if(last !== false){
61453                         this.last = last;
61454                     }
61455                 }else{
61456                     this.selectFirstRow();
61457                 }
61458                 this.fireEvent("afterselectionchange", this);
61459             },
61460             "down" : function(e){
61461                 if(!e.shiftKey){
61462                     this.selectNext(e.shiftKey);
61463                 }else if(this.last !== false && this.lastActive !== false){
61464                     var last = this.last;
61465                     this.selectRange(this.last,  this.lastActive+1);
61466                     view.focusRow(this.lastActive);
61467                     if(last !== false){
61468                         this.last = last;
61469                     }
61470                 }else{
61471                     this.selectFirstRow();
61472                 }
61473                 this.fireEvent("afterselectionchange", this);
61474             },
61475             scope: this
61476         });
61477
61478          
61479         view.on("refresh", this.onRefresh, this);
61480         view.on("rowupdated", this.onRowUpdated, this);
61481         view.on("rowremoved", this.onRemove, this);
61482     },
61483
61484     // private
61485     onRefresh : function(){
61486         var ds = this.grid.ds, i, v = this.grid.view;
61487         var s = this.selections;
61488         s.each(function(r){
61489             if((i = ds.indexOfId(r.id)) != -1){
61490                 v.onRowSelect(i);
61491                 s.add(ds.getAt(i)); // updating the selection relate data
61492             }else{
61493                 s.remove(r);
61494             }
61495         });
61496     },
61497
61498     // private
61499     onRemove : function(v, index, r){
61500         this.selections.remove(r);
61501     },
61502
61503     // private
61504     onRowUpdated : function(v, index, r){
61505         if(this.isSelected(r)){
61506             v.onRowSelect(index);
61507         }
61508     },
61509
61510     /**
61511      * Select records.
61512      * @param {Array} records The records to select
61513      * @param {Boolean} keepExisting (optional) True to keep existing selections
61514      */
61515     selectRecords : function(records, keepExisting){
61516         if(!keepExisting){
61517             this.clearSelections();
61518         }
61519         var ds = this.grid.ds;
61520         for(var i = 0, len = records.length; i < len; i++){
61521             this.selectRow(ds.indexOf(records[i]), true);
61522         }
61523     },
61524
61525     /**
61526      * Gets the number of selected rows.
61527      * @return {Number}
61528      */
61529     getCount : function(){
61530         return this.selections.length;
61531     },
61532
61533     /**
61534      * Selects the first row in the grid.
61535      */
61536     selectFirstRow : function(){
61537         this.selectRow(0);
61538     },
61539
61540     /**
61541      * Select the last row.
61542      * @param {Boolean} keepExisting (optional) True to keep existing selections
61543      */
61544     selectLastRow : function(keepExisting){
61545         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61546     },
61547
61548     /**
61549      * Selects the row immediately following the last selected row.
61550      * @param {Boolean} keepExisting (optional) True to keep existing selections
61551      */
61552     selectNext : function(keepExisting){
61553         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61554             this.selectRow(this.last+1, keepExisting);
61555             var view = this.grid.view ? this.grid.view : this.grid;
61556             view.focusRow(this.last);
61557         }
61558     },
61559
61560     /**
61561      * Selects the row that precedes the last selected row.
61562      * @param {Boolean} keepExisting (optional) True to keep existing selections
61563      */
61564     selectPrevious : function(keepExisting){
61565         if(this.last){
61566             this.selectRow(this.last-1, keepExisting);
61567             var view = this.grid.view ? this.grid.view : this.grid;
61568             view.focusRow(this.last);
61569         }
61570     },
61571
61572     /**
61573      * Returns the selected records
61574      * @return {Array} Array of selected records
61575      */
61576     getSelections : function(){
61577         return [].concat(this.selections.items);
61578     },
61579
61580     /**
61581      * Returns the first selected record.
61582      * @return {Record}
61583      */
61584     getSelected : function(){
61585         return this.selections.itemAt(0);
61586     },
61587
61588
61589     /**
61590      * Clears all selections.
61591      */
61592     clearSelections : function(fast){
61593         if(this.locked) {
61594             return;
61595         }
61596         if(fast !== true){
61597             var ds = this.grid.ds;
61598             var s = this.selections;
61599             s.each(function(r){
61600                 this.deselectRow(ds.indexOfId(r.id));
61601             }, this);
61602             s.clear();
61603         }else{
61604             this.selections.clear();
61605         }
61606         this.last = false;
61607     },
61608
61609
61610     /**
61611      * Selects all rows.
61612      */
61613     selectAll : function(){
61614         if(this.locked) {
61615             return;
61616         }
61617         this.selections.clear();
61618         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61619             this.selectRow(i, true);
61620         }
61621     },
61622
61623     /**
61624      * Returns True if there is a selection.
61625      * @return {Boolean}
61626      */
61627     hasSelection : function(){
61628         return this.selections.length > 0;
61629     },
61630
61631     /**
61632      * Returns True if the specified row is selected.
61633      * @param {Number/Record} record The record or index of the record to check
61634      * @return {Boolean}
61635      */
61636     isSelected : function(index){
61637         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61638         return (r && this.selections.key(r.id) ? true : false);
61639     },
61640
61641     /**
61642      * Returns True if the specified record id is selected.
61643      * @param {String} id The id of record to check
61644      * @return {Boolean}
61645      */
61646     isIdSelected : function(id){
61647         return (this.selections.key(id) ? true : false);
61648     },
61649
61650     // private
61651     handleMouseDown : function(e, t)
61652     {
61653         var view = this.grid.view ? this.grid.view : this.grid;
61654         var rowIndex;
61655         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61656             return;
61657         };
61658         if(e.shiftKey && this.last !== false){
61659             var last = this.last;
61660             this.selectRange(last, rowIndex, e.ctrlKey);
61661             this.last = last; // reset the last
61662             view.focusRow(rowIndex);
61663         }else{
61664             var isSelected = this.isSelected(rowIndex);
61665             if(e.button !== 0 && isSelected){
61666                 view.focusRow(rowIndex);
61667             }else if(e.ctrlKey && isSelected){
61668                 this.deselectRow(rowIndex);
61669             }else if(!isSelected){
61670                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61671                 view.focusRow(rowIndex);
61672             }
61673         }
61674         this.fireEvent("afterselectionchange", this);
61675     },
61676     // private
61677     handleDragableRowClick :  function(grid, rowIndex, e) 
61678     {
61679         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61680             this.selectRow(rowIndex, false);
61681             var view = this.grid.view ? this.grid.view : this.grid;
61682             view.focusRow(rowIndex);
61683              this.fireEvent("afterselectionchange", this);
61684         }
61685     },
61686     
61687     /**
61688      * Selects multiple rows.
61689      * @param {Array} rows Array of the indexes of the row to select
61690      * @param {Boolean} keepExisting (optional) True to keep existing selections
61691      */
61692     selectRows : function(rows, keepExisting){
61693         if(!keepExisting){
61694             this.clearSelections();
61695         }
61696         for(var i = 0, len = rows.length; i < len; i++){
61697             this.selectRow(rows[i], true);
61698         }
61699     },
61700
61701     /**
61702      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61703      * @param {Number} startRow The index of the first row in the range
61704      * @param {Number} endRow The index of the last row in the range
61705      * @param {Boolean} keepExisting (optional) True to retain existing selections
61706      */
61707     selectRange : function(startRow, endRow, keepExisting){
61708         if(this.locked) {
61709             return;
61710         }
61711         if(!keepExisting){
61712             this.clearSelections();
61713         }
61714         if(startRow <= endRow){
61715             for(var i = startRow; i <= endRow; i++){
61716                 this.selectRow(i, true);
61717             }
61718         }else{
61719             for(var i = startRow; i >= endRow; i--){
61720                 this.selectRow(i, true);
61721             }
61722         }
61723     },
61724
61725     /**
61726      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61727      * @param {Number} startRow The index of the first row in the range
61728      * @param {Number} endRow The index of the last row in the range
61729      */
61730     deselectRange : function(startRow, endRow, preventViewNotify){
61731         if(this.locked) {
61732             return;
61733         }
61734         for(var i = startRow; i <= endRow; i++){
61735             this.deselectRow(i, preventViewNotify);
61736         }
61737     },
61738
61739     /**
61740      * Selects a row.
61741      * @param {Number} row The index of the row to select
61742      * @param {Boolean} keepExisting (optional) True to keep existing selections
61743      */
61744     selectRow : function(index, keepExisting, preventViewNotify){
61745         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61746             return;
61747         }
61748         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61749             if(!keepExisting || this.singleSelect){
61750                 this.clearSelections();
61751             }
61752             var r = this.grid.ds.getAt(index);
61753             this.selections.add(r);
61754             this.last = this.lastActive = index;
61755             if(!preventViewNotify){
61756                 var view = this.grid.view ? this.grid.view : this.grid;
61757                 view.onRowSelect(index);
61758             }
61759             this.fireEvent("rowselect", this, index, r);
61760             this.fireEvent("selectionchange", this);
61761         }
61762     },
61763
61764     /**
61765      * Deselects a row.
61766      * @param {Number} row The index of the row to deselect
61767      */
61768     deselectRow : function(index, preventViewNotify){
61769         if(this.locked) {
61770             return;
61771         }
61772         if(this.last == index){
61773             this.last = false;
61774         }
61775         if(this.lastActive == index){
61776             this.lastActive = false;
61777         }
61778         var r = this.grid.ds.getAt(index);
61779         this.selections.remove(r);
61780         if(!preventViewNotify){
61781             var view = this.grid.view ? this.grid.view : this.grid;
61782             view.onRowDeselect(index);
61783         }
61784         this.fireEvent("rowdeselect", this, index);
61785         this.fireEvent("selectionchange", this);
61786     },
61787
61788     // private
61789     restoreLast : function(){
61790         if(this._last){
61791             this.last = this._last;
61792         }
61793     },
61794
61795     // private
61796     acceptsNav : function(row, col, cm){
61797         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61798     },
61799
61800     // private
61801     onEditorKey : function(field, e){
61802         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61803         if(k == e.TAB){
61804             e.stopEvent();
61805             ed.completeEdit();
61806             if(e.shiftKey){
61807                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61808             }else{
61809                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61810             }
61811         }else if(k == e.ENTER && !e.ctrlKey){
61812             e.stopEvent();
61813             ed.completeEdit();
61814             if(e.shiftKey){
61815                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61816             }else{
61817                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61818             }
61819         }else if(k == e.ESC){
61820             ed.cancelEdit();
61821         }
61822         if(newCell){
61823             g.startEditing(newCell[0], newCell[1]);
61824         }
61825     }
61826 });/*
61827  * Based on:
61828  * Ext JS Library 1.1.1
61829  * Copyright(c) 2006-2007, Ext JS, LLC.
61830  *
61831  * Originally Released Under LGPL - original licence link has changed is not relivant.
61832  *
61833  * Fork - LGPL
61834  * <script type="text/javascript">
61835  */
61836 /**
61837  * @class Roo.grid.CellSelectionModel
61838  * @extends Roo.grid.AbstractSelectionModel
61839  * This class provides the basic implementation for cell selection in a grid.
61840  * @constructor
61841  * @param {Object} config The object containing the configuration of this model.
61842  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61843  */
61844 Roo.grid.CellSelectionModel = function(config){
61845     Roo.apply(this, config);
61846
61847     this.selection = null;
61848
61849     this.addEvents({
61850         /**
61851              * @event beforerowselect
61852              * Fires before a cell is selected.
61853              * @param {SelectionModel} this
61854              * @param {Number} rowIndex The selected row index
61855              * @param {Number} colIndex The selected cell index
61856              */
61857             "beforecellselect" : true,
61858         /**
61859              * @event cellselect
61860              * Fires when a cell is selected.
61861              * @param {SelectionModel} this
61862              * @param {Number} rowIndex The selected row index
61863              * @param {Number} colIndex The selected cell index
61864              */
61865             "cellselect" : true,
61866         /**
61867              * @event selectionchange
61868              * Fires when the active selection changes.
61869              * @param {SelectionModel} this
61870              * @param {Object} selection null for no selection or an object (o) with two properties
61871                 <ul>
61872                 <li>o.record: the record object for the row the selection is in</li>
61873                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61874                 </ul>
61875              */
61876             "selectionchange" : true,
61877         /**
61878              * @event tabend
61879              * Fires when the tab (or enter) was pressed on the last editable cell
61880              * You can use this to trigger add new row.
61881              * @param {SelectionModel} this
61882              */
61883             "tabend" : true,
61884          /**
61885              * @event beforeeditnext
61886              * Fires before the next editable sell is made active
61887              * You can use this to skip to another cell or fire the tabend
61888              *    if you set cell to false
61889              * @param {Object} eventdata object : { cell : [ row, col ] } 
61890              */
61891             "beforeeditnext" : true
61892     });
61893     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61894 };
61895
61896 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61897     
61898     enter_is_tab: false,
61899
61900     /** @ignore */
61901     initEvents : function(){
61902         this.grid.on("mousedown", this.handleMouseDown, this);
61903         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61904         var view = this.grid.view;
61905         view.on("refresh", this.onViewChange, this);
61906         view.on("rowupdated", this.onRowUpdated, this);
61907         view.on("beforerowremoved", this.clearSelections, this);
61908         view.on("beforerowsinserted", this.clearSelections, this);
61909         if(this.grid.isEditor){
61910             this.grid.on("beforeedit", this.beforeEdit,  this);
61911         }
61912     },
61913
61914         //private
61915     beforeEdit : function(e){
61916         this.select(e.row, e.column, false, true, e.record);
61917     },
61918
61919         //private
61920     onRowUpdated : function(v, index, r){
61921         if(this.selection && this.selection.record == r){
61922             v.onCellSelect(index, this.selection.cell[1]);
61923         }
61924     },
61925
61926         //private
61927     onViewChange : function(){
61928         this.clearSelections(true);
61929     },
61930
61931         /**
61932          * Returns the currently selected cell,.
61933          * @return {Array} The selected cell (row, column) or null if none selected.
61934          */
61935     getSelectedCell : function(){
61936         return this.selection ? this.selection.cell : null;
61937     },
61938
61939     /**
61940      * Clears all selections.
61941      * @param {Boolean} true to prevent the gridview from being notified about the change.
61942      */
61943     clearSelections : function(preventNotify){
61944         var s = this.selection;
61945         if(s){
61946             if(preventNotify !== true){
61947                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61948             }
61949             this.selection = null;
61950             this.fireEvent("selectionchange", this, null);
61951         }
61952     },
61953
61954     /**
61955      * Returns true if there is a selection.
61956      * @return {Boolean}
61957      */
61958     hasSelection : function(){
61959         return this.selection ? true : false;
61960     },
61961
61962     /** @ignore */
61963     handleMouseDown : function(e, t){
61964         var v = this.grid.getView();
61965         if(this.isLocked()){
61966             return;
61967         };
61968         var row = v.findRowIndex(t);
61969         var cell = v.findCellIndex(t);
61970         if(row !== false && cell !== false){
61971             this.select(row, cell);
61972         }
61973     },
61974
61975     /**
61976      * Selects a cell.
61977      * @param {Number} rowIndex
61978      * @param {Number} collIndex
61979      */
61980     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
61981         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
61982             this.clearSelections();
61983             r = r || this.grid.dataSource.getAt(rowIndex);
61984             this.selection = {
61985                 record : r,
61986                 cell : [rowIndex, colIndex]
61987             };
61988             if(!preventViewNotify){
61989                 var v = this.grid.getView();
61990                 v.onCellSelect(rowIndex, colIndex);
61991                 if(preventFocus !== true){
61992                     v.focusCell(rowIndex, colIndex);
61993                 }
61994             }
61995             this.fireEvent("cellselect", this, rowIndex, colIndex);
61996             this.fireEvent("selectionchange", this, this.selection);
61997         }
61998     },
61999
62000         //private
62001     isSelectable : function(rowIndex, colIndex, cm){
62002         return !cm.isHidden(colIndex);
62003     },
62004
62005     /** @ignore */
62006     handleKeyDown : function(e){
62007         //Roo.log('Cell Sel Model handleKeyDown');
62008         if(!e.isNavKeyPress()){
62009             return;
62010         }
62011         var g = this.grid, s = this.selection;
62012         if(!s){
62013             e.stopEvent();
62014             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62015             if(cell){
62016                 this.select(cell[0], cell[1]);
62017             }
62018             return;
62019         }
62020         var sm = this;
62021         var walk = function(row, col, step){
62022             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62023         };
62024         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62025         var newCell;
62026
62027       
62028
62029         switch(k){
62030             case e.TAB:
62031                 // handled by onEditorKey
62032                 if (g.isEditor && g.editing) {
62033                     return;
62034                 }
62035                 if(e.shiftKey) {
62036                     newCell = walk(r, c-1, -1);
62037                 } else {
62038                     newCell = walk(r, c+1, 1);
62039                 }
62040                 break;
62041             
62042             case e.DOWN:
62043                newCell = walk(r+1, c, 1);
62044                 break;
62045             
62046             case e.UP:
62047                 newCell = walk(r-1, c, -1);
62048                 break;
62049             
62050             case e.RIGHT:
62051                 newCell = walk(r, c+1, 1);
62052                 break;
62053             
62054             case e.LEFT:
62055                 newCell = walk(r, c-1, -1);
62056                 break;
62057             
62058             case e.ENTER:
62059                 
62060                 if(g.isEditor && !g.editing){
62061                    g.startEditing(r, c);
62062                    e.stopEvent();
62063                    return;
62064                 }
62065                 
62066                 
62067              break;
62068         };
62069         if(newCell){
62070             this.select(newCell[0], newCell[1]);
62071             e.stopEvent();
62072             
62073         }
62074     },
62075
62076     acceptsNav : function(row, col, cm){
62077         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62078     },
62079     /**
62080      * Selects a cell.
62081      * @param {Number} field (not used) - as it's normally used as a listener
62082      * @param {Number} e - event - fake it by using
62083      *
62084      * var e = Roo.EventObjectImpl.prototype;
62085      * e.keyCode = e.TAB
62086      *
62087      * 
62088      */
62089     onEditorKey : function(field, e){
62090         
62091         var k = e.getKey(),
62092             newCell,
62093             g = this.grid,
62094             ed = g.activeEditor,
62095             forward = false;
62096         ///Roo.log('onEditorKey' + k);
62097         
62098         
62099         if (this.enter_is_tab && k == e.ENTER) {
62100             k = e.TAB;
62101         }
62102         
62103         if(k == e.TAB){
62104             if(e.shiftKey){
62105                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62106             }else{
62107                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62108                 forward = true;
62109             }
62110             
62111             e.stopEvent();
62112             
62113         } else if(k == e.ENTER &&  !e.ctrlKey){
62114             ed.completeEdit();
62115             e.stopEvent();
62116             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62117         
62118                 } else if(k == e.ESC){
62119             ed.cancelEdit();
62120         }
62121                 
62122         if (newCell) {
62123             var ecall = { cell : newCell, forward : forward };
62124             this.fireEvent('beforeeditnext', ecall );
62125             newCell = ecall.cell;
62126                         forward = ecall.forward;
62127         }
62128                 
62129         if(newCell){
62130             //Roo.log('next cell after edit');
62131             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62132         } else if (forward) {
62133             // tabbed past last
62134             this.fireEvent.defer(100, this, ['tabend',this]);
62135         }
62136     }
62137 });/*
62138  * Based on:
62139  * Ext JS Library 1.1.1
62140  * Copyright(c) 2006-2007, Ext JS, LLC.
62141  *
62142  * Originally Released Under LGPL - original licence link has changed is not relivant.
62143  *
62144  * Fork - LGPL
62145  * <script type="text/javascript">
62146  */
62147  
62148 /**
62149  * @class Roo.grid.EditorGrid
62150  * @extends Roo.grid.Grid
62151  * Class for creating and editable grid.
62152  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62153  * The container MUST have some type of size defined for the grid to fill. The container will be 
62154  * automatically set to position relative if it isn't already.
62155  * @param {Object} dataSource The data model to bind to
62156  * @param {Object} colModel The column model with info about this grid's columns
62157  */
62158 Roo.grid.EditorGrid = function(container, config){
62159     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62160     this.getGridEl().addClass("xedit-grid");
62161
62162     if(!this.selModel){
62163         this.selModel = new Roo.grid.CellSelectionModel();
62164     }
62165
62166     this.activeEditor = null;
62167
62168         this.addEvents({
62169             /**
62170              * @event beforeedit
62171              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62172              * <ul style="padding:5px;padding-left:16px;">
62173              * <li>grid - This grid</li>
62174              * <li>record - The record being edited</li>
62175              * <li>field - The field name being edited</li>
62176              * <li>value - The value for the field being edited.</li>
62177              * <li>row - The grid row index</li>
62178              * <li>column - The grid column index</li>
62179              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62180              * </ul>
62181              * @param {Object} e An edit event (see above for description)
62182              */
62183             "beforeedit" : true,
62184             /**
62185              * @event afteredit
62186              * Fires after a cell is edited. <br />
62187              * <ul style="padding:5px;padding-left:16px;">
62188              * <li>grid - This grid</li>
62189              * <li>record - The record being edited</li>
62190              * <li>field - The field name being edited</li>
62191              * <li>value - The value being set</li>
62192              * <li>originalValue - The original value for the field, before the edit.</li>
62193              * <li>row - The grid row index</li>
62194              * <li>column - The grid column index</li>
62195              * </ul>
62196              * @param {Object} e An edit event (see above for description)
62197              */
62198             "afteredit" : true,
62199             /**
62200              * @event validateedit
62201              * Fires after a cell is edited, but before the value is set in the record. 
62202          * You can use this to modify the value being set in the field, Return false
62203              * to cancel the change. The edit event object has the following properties <br />
62204              * <ul style="padding:5px;padding-left:16px;">
62205          * <li>editor - This editor</li>
62206              * <li>grid - This grid</li>
62207              * <li>record - The record being edited</li>
62208              * <li>field - The field name being edited</li>
62209              * <li>value - The value being set</li>
62210              * <li>originalValue - The original value for the field, before the edit.</li>
62211              * <li>row - The grid row index</li>
62212              * <li>column - The grid column index</li>
62213              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62214              * </ul>
62215              * @param {Object} e An edit event (see above for description)
62216              */
62217             "validateedit" : true
62218         });
62219     this.on("bodyscroll", this.stopEditing,  this);
62220     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62221 };
62222
62223 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62224     /**
62225      * @cfg {Number} clicksToEdit
62226      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62227      */
62228     clicksToEdit: 2,
62229
62230     // private
62231     isEditor : true,
62232     // private
62233     trackMouseOver: false, // causes very odd FF errors
62234
62235     onCellDblClick : function(g, row, col){
62236         this.startEditing(row, col);
62237     },
62238
62239     onEditComplete : function(ed, value, startValue){
62240         this.editing = false;
62241         this.activeEditor = null;
62242         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62243         var r = ed.record;
62244         var field = this.colModel.getDataIndex(ed.col);
62245         var e = {
62246             grid: this,
62247             record: r,
62248             field: field,
62249             originalValue: startValue,
62250             value: value,
62251             row: ed.row,
62252             column: ed.col,
62253             cancel:false,
62254             editor: ed
62255         };
62256         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62257         cell.show();
62258           
62259         if(String(value) !== String(startValue)){
62260             
62261             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62262                 r.set(field, e.value);
62263                 // if we are dealing with a combo box..
62264                 // then we also set the 'name' colum to be the displayField
62265                 if (ed.field.displayField && ed.field.name) {
62266                     r.set(ed.field.name, ed.field.el.dom.value);
62267                 }
62268                 
62269                 delete e.cancel; //?? why!!!
62270                 this.fireEvent("afteredit", e);
62271             }
62272         } else {
62273             this.fireEvent("afteredit", e); // always fire it!
62274         }
62275         this.view.focusCell(ed.row, ed.col);
62276     },
62277
62278     /**
62279      * Starts editing the specified for the specified row/column
62280      * @param {Number} rowIndex
62281      * @param {Number} colIndex
62282      */
62283     startEditing : function(row, col){
62284         this.stopEditing();
62285         if(this.colModel.isCellEditable(col, row)){
62286             this.view.ensureVisible(row, col, true);
62287           
62288             var r = this.dataSource.getAt(row);
62289             var field = this.colModel.getDataIndex(col);
62290             var cell = Roo.get(this.view.getCell(row,col));
62291             var e = {
62292                 grid: this,
62293                 record: r,
62294                 field: field,
62295                 value: r.data[field],
62296                 row: row,
62297                 column: col,
62298                 cancel:false 
62299             };
62300             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62301                 this.editing = true;
62302                 var ed = this.colModel.getCellEditor(col, row);
62303                 
62304                 if (!ed) {
62305                     return;
62306                 }
62307                 if(!ed.rendered){
62308                     ed.render(ed.parentEl || document.body);
62309                 }
62310                 ed.field.reset();
62311                
62312                 cell.hide();
62313                 
62314                 (function(){ // complex but required for focus issues in safari, ie and opera
62315                     ed.row = row;
62316                     ed.col = col;
62317                     ed.record = r;
62318                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62319                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62320                     this.activeEditor = ed;
62321                     var v = r.data[field];
62322                     ed.startEdit(this.view.getCell(row, col), v);
62323                     // combo's with 'displayField and name set
62324                     if (ed.field.displayField && ed.field.name) {
62325                         ed.field.el.dom.value = r.data[ed.field.name];
62326                     }
62327                     
62328                     
62329                 }).defer(50, this);
62330             }
62331         }
62332     },
62333         
62334     /**
62335      * Stops any active editing
62336      */
62337     stopEditing : function(){
62338         if(this.activeEditor){
62339             this.activeEditor.completeEdit();
62340         }
62341         this.activeEditor = null;
62342     },
62343         
62344          /**
62345      * Called to get grid's drag proxy text, by default returns this.ddText.
62346      * @return {String}
62347      */
62348     getDragDropText : function(){
62349         var count = this.selModel.getSelectedCell() ? 1 : 0;
62350         return String.format(this.ddText, count, count == 1 ? '' : 's');
62351     }
62352         
62353 });/*
62354  * Based on:
62355  * Ext JS Library 1.1.1
62356  * Copyright(c) 2006-2007, Ext JS, LLC.
62357  *
62358  * Originally Released Under LGPL - original licence link has changed is not relivant.
62359  *
62360  * Fork - LGPL
62361  * <script type="text/javascript">
62362  */
62363
62364 // private - not really -- you end up using it !
62365 // This is a support class used internally by the Grid components
62366
62367 /**
62368  * @class Roo.grid.GridEditor
62369  * @extends Roo.Editor
62370  * Class for creating and editable grid elements.
62371  * @param {Object} config any settings (must include field)
62372  */
62373 Roo.grid.GridEditor = function(field, config){
62374     if (!config && field.field) {
62375         config = field;
62376         field = Roo.factory(config.field, Roo.form);
62377     }
62378     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62379     field.monitorTab = false;
62380 };
62381
62382 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62383     
62384     /**
62385      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62386      */
62387     
62388     alignment: "tl-tl",
62389     autoSize: "width",
62390     hideEl : false,
62391     cls: "x-small-editor x-grid-editor",
62392     shim:false,
62393     shadow:"frame"
62394 });/*
62395  * Based on:
62396  * Ext JS Library 1.1.1
62397  * Copyright(c) 2006-2007, Ext JS, LLC.
62398  *
62399  * Originally Released Under LGPL - original licence link has changed is not relivant.
62400  *
62401  * Fork - LGPL
62402  * <script type="text/javascript">
62403  */
62404   
62405
62406   
62407 Roo.grid.PropertyRecord = Roo.data.Record.create([
62408     {name:'name',type:'string'},  'value'
62409 ]);
62410
62411
62412 Roo.grid.PropertyStore = function(grid, source){
62413     this.grid = grid;
62414     this.store = new Roo.data.Store({
62415         recordType : Roo.grid.PropertyRecord
62416     });
62417     this.store.on('update', this.onUpdate,  this);
62418     if(source){
62419         this.setSource(source);
62420     }
62421     Roo.grid.PropertyStore.superclass.constructor.call(this);
62422 };
62423
62424
62425
62426 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62427     setSource : function(o){
62428         this.source = o;
62429         this.store.removeAll();
62430         var data = [];
62431         for(var k in o){
62432             if(this.isEditableValue(o[k])){
62433                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62434             }
62435         }
62436         this.store.loadRecords({records: data}, {}, true);
62437     },
62438
62439     onUpdate : function(ds, record, type){
62440         if(type == Roo.data.Record.EDIT){
62441             var v = record.data['value'];
62442             var oldValue = record.modified['value'];
62443             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62444                 this.source[record.id] = v;
62445                 record.commit();
62446                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62447             }else{
62448                 record.reject();
62449             }
62450         }
62451     },
62452
62453     getProperty : function(row){
62454        return this.store.getAt(row);
62455     },
62456
62457     isEditableValue: function(val){
62458         if(val && val instanceof Date){
62459             return true;
62460         }else if(typeof val == 'object' || typeof val == 'function'){
62461             return false;
62462         }
62463         return true;
62464     },
62465
62466     setValue : function(prop, value){
62467         this.source[prop] = value;
62468         this.store.getById(prop).set('value', value);
62469     },
62470
62471     getSource : function(){
62472         return this.source;
62473     }
62474 });
62475
62476 Roo.grid.PropertyColumnModel = function(grid, store){
62477     this.grid = grid;
62478     var g = Roo.grid;
62479     g.PropertyColumnModel.superclass.constructor.call(this, [
62480         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62481         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62482     ]);
62483     this.store = store;
62484     this.bselect = Roo.DomHelper.append(document.body, {
62485         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62486             {tag: 'option', value: 'true', html: 'true'},
62487             {tag: 'option', value: 'false', html: 'false'}
62488         ]
62489     });
62490     Roo.id(this.bselect);
62491     var f = Roo.form;
62492     this.editors = {
62493         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62494         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62495         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62496         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62497         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62498     };
62499     this.renderCellDelegate = this.renderCell.createDelegate(this);
62500     this.renderPropDelegate = this.renderProp.createDelegate(this);
62501 };
62502
62503 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62504     
62505     
62506     nameText : 'Name',
62507     valueText : 'Value',
62508     
62509     dateFormat : 'm/j/Y',
62510     
62511     
62512     renderDate : function(dateVal){
62513         return dateVal.dateFormat(this.dateFormat);
62514     },
62515
62516     renderBool : function(bVal){
62517         return bVal ? 'true' : 'false';
62518     },
62519
62520     isCellEditable : function(colIndex, rowIndex){
62521         return colIndex == 1;
62522     },
62523
62524     getRenderer : function(col){
62525         return col == 1 ?
62526             this.renderCellDelegate : this.renderPropDelegate;
62527     },
62528
62529     renderProp : function(v){
62530         return this.getPropertyName(v);
62531     },
62532
62533     renderCell : function(val){
62534         var rv = val;
62535         if(val instanceof Date){
62536             rv = this.renderDate(val);
62537         }else if(typeof val == 'boolean'){
62538             rv = this.renderBool(val);
62539         }
62540         return Roo.util.Format.htmlEncode(rv);
62541     },
62542
62543     getPropertyName : function(name){
62544         var pn = this.grid.propertyNames;
62545         return pn && pn[name] ? pn[name] : name;
62546     },
62547
62548     getCellEditor : function(colIndex, rowIndex){
62549         var p = this.store.getProperty(rowIndex);
62550         var n = p.data['name'], val = p.data['value'];
62551         
62552         if(typeof(this.grid.customEditors[n]) == 'string'){
62553             return this.editors[this.grid.customEditors[n]];
62554         }
62555         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62556             return this.grid.customEditors[n];
62557         }
62558         if(val instanceof Date){
62559             return this.editors['date'];
62560         }else if(typeof val == 'number'){
62561             return this.editors['number'];
62562         }else if(typeof val == 'boolean'){
62563             return this.editors['boolean'];
62564         }else{
62565             return this.editors['string'];
62566         }
62567     }
62568 });
62569
62570 /**
62571  * @class Roo.grid.PropertyGrid
62572  * @extends Roo.grid.EditorGrid
62573  * This class represents the  interface of a component based property grid control.
62574  * <br><br>Usage:<pre><code>
62575  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62576       
62577  });
62578  // set any options
62579  grid.render();
62580  * </code></pre>
62581   
62582  * @constructor
62583  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62584  * The container MUST have some type of size defined for the grid to fill. The container will be
62585  * automatically set to position relative if it isn't already.
62586  * @param {Object} config A config object that sets properties on this grid.
62587  */
62588 Roo.grid.PropertyGrid = function(container, config){
62589     config = config || {};
62590     var store = new Roo.grid.PropertyStore(this);
62591     this.store = store;
62592     var cm = new Roo.grid.PropertyColumnModel(this, store);
62593     store.store.sort('name', 'ASC');
62594     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62595         ds: store.store,
62596         cm: cm,
62597         enableColLock:false,
62598         enableColumnMove:false,
62599         stripeRows:false,
62600         trackMouseOver: false,
62601         clicksToEdit:1
62602     }, config));
62603     this.getGridEl().addClass('x-props-grid');
62604     this.lastEditRow = null;
62605     this.on('columnresize', this.onColumnResize, this);
62606     this.addEvents({
62607          /**
62608              * @event beforepropertychange
62609              * Fires before a property changes (return false to stop?)
62610              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62611              * @param {String} id Record Id
62612              * @param {String} newval New Value
62613          * @param {String} oldval Old Value
62614              */
62615         "beforepropertychange": true,
62616         /**
62617              * @event propertychange
62618              * Fires after a property changes
62619              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62620              * @param {String} id Record Id
62621              * @param {String} newval New Value
62622          * @param {String} oldval Old Value
62623              */
62624         "propertychange": true
62625     });
62626     this.customEditors = this.customEditors || {};
62627 };
62628 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62629     
62630      /**
62631      * @cfg {Object} customEditors map of colnames=> custom editors.
62632      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62633      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62634      * false disables editing of the field.
62635          */
62636     
62637       /**
62638      * @cfg {Object} propertyNames map of property Names to their displayed value
62639          */
62640     
62641     render : function(){
62642         Roo.grid.PropertyGrid.superclass.render.call(this);
62643         this.autoSize.defer(100, this);
62644     },
62645
62646     autoSize : function(){
62647         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62648         if(this.view){
62649             this.view.fitColumns();
62650         }
62651     },
62652
62653     onColumnResize : function(){
62654         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62655         this.autoSize();
62656     },
62657     /**
62658      * Sets the data for the Grid
62659      * accepts a Key => Value object of all the elements avaiable.
62660      * @param {Object} data  to appear in grid.
62661      */
62662     setSource : function(source){
62663         this.store.setSource(source);
62664         //this.autoSize();
62665     },
62666     /**
62667      * Gets all the data from the grid.
62668      * @return {Object} data  data stored in grid
62669      */
62670     getSource : function(){
62671         return this.store.getSource();
62672     }
62673 });/*
62674   
62675  * Licence LGPL
62676  
62677  */
62678  
62679 /**
62680  * @class Roo.grid.Calendar
62681  * @extends Roo.grid.Grid
62682  * This class extends the Grid to provide a calendar widget
62683  * <br><br>Usage:<pre><code>
62684  var grid = new Roo.grid.Calendar("my-container-id", {
62685      ds: myDataStore,
62686      cm: myColModel,
62687      selModel: mySelectionModel,
62688      autoSizeColumns: true,
62689      monitorWindowResize: false,
62690      trackMouseOver: true
62691      eventstore : real data store..
62692  });
62693  // set any options
62694  grid.render();
62695   
62696   * @constructor
62697  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62698  * The container MUST have some type of size defined for the grid to fill. The container will be
62699  * automatically set to position relative if it isn't already.
62700  * @param {Object} config A config object that sets properties on this grid.
62701  */
62702 Roo.grid.Calendar = function(container, config){
62703         // initialize the container
62704         this.container = Roo.get(container);
62705         this.container.update("");
62706         this.container.setStyle("overflow", "hidden");
62707     this.container.addClass('x-grid-container');
62708
62709     this.id = this.container.id;
62710
62711     Roo.apply(this, config);
62712     // check and correct shorthanded configs
62713     
62714     var rows = [];
62715     var d =1;
62716     for (var r = 0;r < 6;r++) {
62717         
62718         rows[r]=[];
62719         for (var c =0;c < 7;c++) {
62720             rows[r][c]= '';
62721         }
62722     }
62723     if (this.eventStore) {
62724         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62725         this.eventStore.on('load',this.onLoad, this);
62726         this.eventStore.on('beforeload',this.clearEvents, this);
62727          
62728     }
62729     
62730     this.dataSource = new Roo.data.Store({
62731             proxy: new Roo.data.MemoryProxy(rows),
62732             reader: new Roo.data.ArrayReader({}, [
62733                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62734     });
62735
62736     this.dataSource.load();
62737     this.ds = this.dataSource;
62738     this.ds.xmodule = this.xmodule || false;
62739     
62740     
62741     var cellRender = function(v,x,r)
62742     {
62743         return String.format(
62744             '<div class="fc-day  fc-widget-content"><div>' +
62745                 '<div class="fc-event-container"></div>' +
62746                 '<div class="fc-day-number">{0}</div>'+
62747                 
62748                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62749             '</div></div>', v);
62750     
62751     }
62752     
62753     
62754     this.colModel = new Roo.grid.ColumnModel( [
62755         {
62756             xtype: 'ColumnModel',
62757             xns: Roo.grid,
62758             dataIndex : 'weekday0',
62759             header : 'Sunday',
62760             renderer : cellRender
62761         },
62762         {
62763             xtype: 'ColumnModel',
62764             xns: Roo.grid,
62765             dataIndex : 'weekday1',
62766             header : 'Monday',
62767             renderer : cellRender
62768         },
62769         {
62770             xtype: 'ColumnModel',
62771             xns: Roo.grid,
62772             dataIndex : 'weekday2',
62773             header : 'Tuesday',
62774             renderer : cellRender
62775         },
62776         {
62777             xtype: 'ColumnModel',
62778             xns: Roo.grid,
62779             dataIndex : 'weekday3',
62780             header : 'Wednesday',
62781             renderer : cellRender
62782         },
62783         {
62784             xtype: 'ColumnModel',
62785             xns: Roo.grid,
62786             dataIndex : 'weekday4',
62787             header : 'Thursday',
62788             renderer : cellRender
62789         },
62790         {
62791             xtype: 'ColumnModel',
62792             xns: Roo.grid,
62793             dataIndex : 'weekday5',
62794             header : 'Friday',
62795             renderer : cellRender
62796         },
62797         {
62798             xtype: 'ColumnModel',
62799             xns: Roo.grid,
62800             dataIndex : 'weekday6',
62801             header : 'Saturday',
62802             renderer : cellRender
62803         }
62804     ]);
62805     this.cm = this.colModel;
62806     this.cm.xmodule = this.xmodule || false;
62807  
62808         
62809           
62810     //this.selModel = new Roo.grid.CellSelectionModel();
62811     //this.sm = this.selModel;
62812     //this.selModel.init(this);
62813     
62814     
62815     if(this.width){
62816         this.container.setWidth(this.width);
62817     }
62818
62819     if(this.height){
62820         this.container.setHeight(this.height);
62821     }
62822     /** @private */
62823         this.addEvents({
62824         // raw events
62825         /**
62826          * @event click
62827          * The raw click event for the entire grid.
62828          * @param {Roo.EventObject} e
62829          */
62830         "click" : true,
62831         /**
62832          * @event dblclick
62833          * The raw dblclick event for the entire grid.
62834          * @param {Roo.EventObject} e
62835          */
62836         "dblclick" : true,
62837         /**
62838          * @event contextmenu
62839          * The raw contextmenu event for the entire grid.
62840          * @param {Roo.EventObject} e
62841          */
62842         "contextmenu" : true,
62843         /**
62844          * @event mousedown
62845          * The raw mousedown event for the entire grid.
62846          * @param {Roo.EventObject} e
62847          */
62848         "mousedown" : true,
62849         /**
62850          * @event mouseup
62851          * The raw mouseup event for the entire grid.
62852          * @param {Roo.EventObject} e
62853          */
62854         "mouseup" : true,
62855         /**
62856          * @event mouseover
62857          * The raw mouseover event for the entire grid.
62858          * @param {Roo.EventObject} e
62859          */
62860         "mouseover" : true,
62861         /**
62862          * @event mouseout
62863          * The raw mouseout event for the entire grid.
62864          * @param {Roo.EventObject} e
62865          */
62866         "mouseout" : true,
62867         /**
62868          * @event keypress
62869          * The raw keypress event for the entire grid.
62870          * @param {Roo.EventObject} e
62871          */
62872         "keypress" : true,
62873         /**
62874          * @event keydown
62875          * The raw keydown event for the entire grid.
62876          * @param {Roo.EventObject} e
62877          */
62878         "keydown" : true,
62879
62880         // custom events
62881
62882         /**
62883          * @event cellclick
62884          * Fires when a cell is clicked
62885          * @param {Grid} this
62886          * @param {Number} rowIndex
62887          * @param {Number} columnIndex
62888          * @param {Roo.EventObject} e
62889          */
62890         "cellclick" : true,
62891         /**
62892          * @event celldblclick
62893          * Fires when a cell is double clicked
62894          * @param {Grid} this
62895          * @param {Number} rowIndex
62896          * @param {Number} columnIndex
62897          * @param {Roo.EventObject} e
62898          */
62899         "celldblclick" : true,
62900         /**
62901          * @event rowclick
62902          * Fires when a row is clicked
62903          * @param {Grid} this
62904          * @param {Number} rowIndex
62905          * @param {Roo.EventObject} e
62906          */
62907         "rowclick" : true,
62908         /**
62909          * @event rowdblclick
62910          * Fires when a row is double clicked
62911          * @param {Grid} this
62912          * @param {Number} rowIndex
62913          * @param {Roo.EventObject} e
62914          */
62915         "rowdblclick" : true,
62916         /**
62917          * @event headerclick
62918          * Fires when a header is clicked
62919          * @param {Grid} this
62920          * @param {Number} columnIndex
62921          * @param {Roo.EventObject} e
62922          */
62923         "headerclick" : true,
62924         /**
62925          * @event headerdblclick
62926          * Fires when a header cell is double clicked
62927          * @param {Grid} this
62928          * @param {Number} columnIndex
62929          * @param {Roo.EventObject} e
62930          */
62931         "headerdblclick" : true,
62932         /**
62933          * @event rowcontextmenu
62934          * Fires when a row is right clicked
62935          * @param {Grid} this
62936          * @param {Number} rowIndex
62937          * @param {Roo.EventObject} e
62938          */
62939         "rowcontextmenu" : true,
62940         /**
62941          * @event cellcontextmenu
62942          * Fires when a cell is right clicked
62943          * @param {Grid} this
62944          * @param {Number} rowIndex
62945          * @param {Number} cellIndex
62946          * @param {Roo.EventObject} e
62947          */
62948          "cellcontextmenu" : true,
62949         /**
62950          * @event headercontextmenu
62951          * Fires when a header is right clicked
62952          * @param {Grid} this
62953          * @param {Number} columnIndex
62954          * @param {Roo.EventObject} e
62955          */
62956         "headercontextmenu" : true,
62957         /**
62958          * @event bodyscroll
62959          * Fires when the body element is scrolled
62960          * @param {Number} scrollLeft
62961          * @param {Number} scrollTop
62962          */
62963         "bodyscroll" : true,
62964         /**
62965          * @event columnresize
62966          * Fires when the user resizes a column
62967          * @param {Number} columnIndex
62968          * @param {Number} newSize
62969          */
62970         "columnresize" : true,
62971         /**
62972          * @event columnmove
62973          * Fires when the user moves a column
62974          * @param {Number} oldIndex
62975          * @param {Number} newIndex
62976          */
62977         "columnmove" : true,
62978         /**
62979          * @event startdrag
62980          * Fires when row(s) start being dragged
62981          * @param {Grid} this
62982          * @param {Roo.GridDD} dd The drag drop object
62983          * @param {event} e The raw browser event
62984          */
62985         "startdrag" : true,
62986         /**
62987          * @event enddrag
62988          * Fires when a drag operation is complete
62989          * @param {Grid} this
62990          * @param {Roo.GridDD} dd The drag drop object
62991          * @param {event} e The raw browser event
62992          */
62993         "enddrag" : true,
62994         /**
62995          * @event dragdrop
62996          * Fires when dragged row(s) are dropped on a valid DD target
62997          * @param {Grid} this
62998          * @param {Roo.GridDD} dd The drag drop object
62999          * @param {String} targetId The target drag drop object
63000          * @param {event} e The raw browser event
63001          */
63002         "dragdrop" : true,
63003         /**
63004          * @event dragover
63005          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63006          * @param {Grid} this
63007          * @param {Roo.GridDD} dd The drag drop object
63008          * @param {String} targetId The target drag drop object
63009          * @param {event} e The raw browser event
63010          */
63011         "dragover" : true,
63012         /**
63013          * @event dragenter
63014          *  Fires when the dragged row(s) first cross another DD target while being dragged
63015          * @param {Grid} this
63016          * @param {Roo.GridDD} dd The drag drop object
63017          * @param {String} targetId The target drag drop object
63018          * @param {event} e The raw browser event
63019          */
63020         "dragenter" : true,
63021         /**
63022          * @event dragout
63023          * Fires when the dragged row(s) leave another DD target while being dragged
63024          * @param {Grid} this
63025          * @param {Roo.GridDD} dd The drag drop object
63026          * @param {String} targetId The target drag drop object
63027          * @param {event} e The raw browser event
63028          */
63029         "dragout" : true,
63030         /**
63031          * @event rowclass
63032          * Fires when a row is rendered, so you can change add a style to it.
63033          * @param {GridView} gridview   The grid view
63034          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63035          */
63036         'rowclass' : true,
63037
63038         /**
63039          * @event render
63040          * Fires when the grid is rendered
63041          * @param {Grid} grid
63042          */
63043         'render' : true,
63044             /**
63045              * @event select
63046              * Fires when a date is selected
63047              * @param {DatePicker} this
63048              * @param {Date} date The selected date
63049              */
63050         'select': true,
63051         /**
63052              * @event monthchange
63053              * Fires when the displayed month changes 
63054              * @param {DatePicker} this
63055              * @param {Date} date The selected month
63056              */
63057         'monthchange': true,
63058         /**
63059              * @event evententer
63060              * Fires when mouse over an event
63061              * @param {Calendar} this
63062              * @param {event} Event
63063              */
63064         'evententer': true,
63065         /**
63066              * @event eventleave
63067              * Fires when the mouse leaves an
63068              * @param {Calendar} this
63069              * @param {event}
63070              */
63071         'eventleave': true,
63072         /**
63073              * @event eventclick
63074              * Fires when the mouse click an
63075              * @param {Calendar} this
63076              * @param {event}
63077              */
63078         'eventclick': true,
63079         /**
63080              * @event eventrender
63081              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63082              * @param {Calendar} this
63083              * @param {data} data to be modified
63084              */
63085         'eventrender': true
63086         
63087     });
63088
63089     Roo.grid.Grid.superclass.constructor.call(this);
63090     this.on('render', function() {
63091         this.view.el.addClass('x-grid-cal'); 
63092         
63093         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63094
63095     },this);
63096     
63097     if (!Roo.grid.Calendar.style) {
63098         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63099             
63100             
63101             '.x-grid-cal .x-grid-col' :  {
63102                 height: 'auto !important',
63103                 'vertical-align': 'top'
63104             },
63105             '.x-grid-cal  .fc-event-hori' : {
63106                 height: '14px'
63107             }
63108              
63109             
63110         }, Roo.id());
63111     }
63112
63113     
63114     
63115 };
63116 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63117     /**
63118      * @cfg {Store} eventStore The store that loads events.
63119      */
63120     eventStore : 25,
63121
63122      
63123     activeDate : false,
63124     startDay : 0,
63125     autoWidth : true,
63126     monitorWindowResize : false,
63127
63128     
63129     resizeColumns : function() {
63130         var col = (this.view.el.getWidth() / 7) - 3;
63131         // loop through cols, and setWidth
63132         for(var i =0 ; i < 7 ; i++){
63133             this.cm.setColumnWidth(i, col);
63134         }
63135     },
63136      setDate :function(date) {
63137         
63138         Roo.log('setDate?');
63139         
63140         this.resizeColumns();
63141         var vd = this.activeDate;
63142         this.activeDate = date;
63143 //        if(vd && this.el){
63144 //            var t = date.getTime();
63145 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63146 //                Roo.log('using add remove');
63147 //                
63148 //                this.fireEvent('monthchange', this, date);
63149 //                
63150 //                this.cells.removeClass("fc-state-highlight");
63151 //                this.cells.each(function(c){
63152 //                   if(c.dateValue == t){
63153 //                       c.addClass("fc-state-highlight");
63154 //                       setTimeout(function(){
63155 //                            try{c.dom.firstChild.focus();}catch(e){}
63156 //                       }, 50);
63157 //                       return false;
63158 //                   }
63159 //                   return true;
63160 //                });
63161 //                return;
63162 //            }
63163 //        }
63164         
63165         var days = date.getDaysInMonth();
63166         
63167         var firstOfMonth = date.getFirstDateOfMonth();
63168         var startingPos = firstOfMonth.getDay()-this.startDay;
63169         
63170         if(startingPos < this.startDay){
63171             startingPos += 7;
63172         }
63173         
63174         var pm = date.add(Date.MONTH, -1);
63175         var prevStart = pm.getDaysInMonth()-startingPos;
63176 //        
63177         
63178         
63179         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63180         
63181         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63182         //this.cells.addClassOnOver('fc-state-hover');
63183         
63184         var cells = this.cells.elements;
63185         var textEls = this.textNodes;
63186         
63187         //Roo.each(cells, function(cell){
63188         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63189         //});
63190         
63191         days += startingPos;
63192
63193         // convert everything to numbers so it's fast
63194         var day = 86400000;
63195         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63196         //Roo.log(d);
63197         //Roo.log(pm);
63198         //Roo.log(prevStart);
63199         
63200         var today = new Date().clearTime().getTime();
63201         var sel = date.clearTime().getTime();
63202         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63203         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63204         var ddMatch = this.disabledDatesRE;
63205         var ddText = this.disabledDatesText;
63206         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63207         var ddaysText = this.disabledDaysText;
63208         var format = this.format;
63209         
63210         var setCellClass = function(cal, cell){
63211             
63212             //Roo.log('set Cell Class');
63213             cell.title = "";
63214             var t = d.getTime();
63215             
63216             //Roo.log(d);
63217             
63218             
63219             cell.dateValue = t;
63220             if(t == today){
63221                 cell.className += " fc-today";
63222                 cell.className += " fc-state-highlight";
63223                 cell.title = cal.todayText;
63224             }
63225             if(t == sel){
63226                 // disable highlight in other month..
63227                 cell.className += " fc-state-highlight";
63228                 
63229             }
63230             // disabling
63231             if(t < min) {
63232                 //cell.className = " fc-state-disabled";
63233                 cell.title = cal.minText;
63234                 return;
63235             }
63236             if(t > max) {
63237                 //cell.className = " fc-state-disabled";
63238                 cell.title = cal.maxText;
63239                 return;
63240             }
63241             if(ddays){
63242                 if(ddays.indexOf(d.getDay()) != -1){
63243                     // cell.title = ddaysText;
63244                    // cell.className = " fc-state-disabled";
63245                 }
63246             }
63247             if(ddMatch && format){
63248                 var fvalue = d.dateFormat(format);
63249                 if(ddMatch.test(fvalue)){
63250                     cell.title = ddText.replace("%0", fvalue);
63251                    cell.className = " fc-state-disabled";
63252                 }
63253             }
63254             
63255             if (!cell.initialClassName) {
63256                 cell.initialClassName = cell.dom.className;
63257             }
63258             
63259             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63260         };
63261
63262         var i = 0;
63263         
63264         for(; i < startingPos; i++) {
63265             cells[i].dayName =  (++prevStart);
63266             Roo.log(textEls[i]);
63267             d.setDate(d.getDate()+1);
63268             
63269             //cells[i].className = "fc-past fc-other-month";
63270             setCellClass(this, cells[i]);
63271         }
63272         
63273         var intDay = 0;
63274         
63275         for(; i < days; i++){
63276             intDay = i - startingPos + 1;
63277             cells[i].dayName =  (intDay);
63278             d.setDate(d.getDate()+1);
63279             
63280             cells[i].className = ''; // "x-date-active";
63281             setCellClass(this, cells[i]);
63282         }
63283         var extraDays = 0;
63284         
63285         for(; i < 42; i++) {
63286             //textEls[i].innerHTML = (++extraDays);
63287             
63288             d.setDate(d.getDate()+1);
63289             cells[i].dayName = (++extraDays);
63290             cells[i].className = "fc-future fc-other-month";
63291             setCellClass(this, cells[i]);
63292         }
63293         
63294         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63295         
63296         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63297         
63298         // this will cause all the cells to mis
63299         var rows= [];
63300         var i =0;
63301         for (var r = 0;r < 6;r++) {
63302             for (var c =0;c < 7;c++) {
63303                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63304             }    
63305         }
63306         
63307         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63308         for(i=0;i<cells.length;i++) {
63309             
63310             this.cells.elements[i].dayName = cells[i].dayName ;
63311             this.cells.elements[i].className = cells[i].className;
63312             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63313             this.cells.elements[i].title = cells[i].title ;
63314             this.cells.elements[i].dateValue = cells[i].dateValue ;
63315         }
63316         
63317         
63318         
63319         
63320         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63321         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63322         
63323         ////if(totalRows != 6){
63324             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63325            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63326        // }
63327         
63328         this.fireEvent('monthchange', this, date);
63329         
63330         
63331     },
63332  /**
63333      * Returns the grid's SelectionModel.
63334      * @return {SelectionModel}
63335      */
63336     getSelectionModel : function(){
63337         if(!this.selModel){
63338             this.selModel = new Roo.grid.CellSelectionModel();
63339         }
63340         return this.selModel;
63341     },
63342
63343     load: function() {
63344         this.eventStore.load()
63345         
63346         
63347         
63348     },
63349     
63350     findCell : function(dt) {
63351         dt = dt.clearTime().getTime();
63352         var ret = false;
63353         this.cells.each(function(c){
63354             //Roo.log("check " +c.dateValue + '?=' + dt);
63355             if(c.dateValue == dt){
63356                 ret = c;
63357                 return false;
63358             }
63359             return true;
63360         });
63361         
63362         return ret;
63363     },
63364     
63365     findCells : function(rec) {
63366         var s = rec.data.start_dt.clone().clearTime().getTime();
63367        // Roo.log(s);
63368         var e= rec.data.end_dt.clone().clearTime().getTime();
63369        // Roo.log(e);
63370         var ret = [];
63371         this.cells.each(function(c){
63372              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63373             
63374             if(c.dateValue > e){
63375                 return ;
63376             }
63377             if(c.dateValue < s){
63378                 return ;
63379             }
63380             ret.push(c);
63381         });
63382         
63383         return ret;    
63384     },
63385     
63386     findBestRow: function(cells)
63387     {
63388         var ret = 0;
63389         
63390         for (var i =0 ; i < cells.length;i++) {
63391             ret  = Math.max(cells[i].rows || 0,ret);
63392         }
63393         return ret;
63394         
63395     },
63396     
63397     
63398     addItem : function(rec)
63399     {
63400         // look for vertical location slot in
63401         var cells = this.findCells(rec);
63402         
63403         rec.row = this.findBestRow(cells);
63404         
63405         // work out the location.
63406         
63407         var crow = false;
63408         var rows = [];
63409         for(var i =0; i < cells.length; i++) {
63410             if (!crow) {
63411                 crow = {
63412                     start : cells[i],
63413                     end :  cells[i]
63414                 };
63415                 continue;
63416             }
63417             if (crow.start.getY() == cells[i].getY()) {
63418                 // on same row.
63419                 crow.end = cells[i];
63420                 continue;
63421             }
63422             // different row.
63423             rows.push(crow);
63424             crow = {
63425                 start: cells[i],
63426                 end : cells[i]
63427             };
63428             
63429         }
63430         
63431         rows.push(crow);
63432         rec.els = [];
63433         rec.rows = rows;
63434         rec.cells = cells;
63435         for (var i = 0; i < cells.length;i++) {
63436             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63437             
63438         }
63439         
63440         
63441     },
63442     
63443     clearEvents: function() {
63444         
63445         if (!this.eventStore.getCount()) {
63446             return;
63447         }
63448         // reset number of rows in cells.
63449         Roo.each(this.cells.elements, function(c){
63450             c.rows = 0;
63451         });
63452         
63453         this.eventStore.each(function(e) {
63454             this.clearEvent(e);
63455         },this);
63456         
63457     },
63458     
63459     clearEvent : function(ev)
63460     {
63461         if (ev.els) {
63462             Roo.each(ev.els, function(el) {
63463                 el.un('mouseenter' ,this.onEventEnter, this);
63464                 el.un('mouseleave' ,this.onEventLeave, this);
63465                 el.remove();
63466             },this);
63467             ev.els = [];
63468         }
63469     },
63470     
63471     
63472     renderEvent : function(ev,ctr) {
63473         if (!ctr) {
63474              ctr = this.view.el.select('.fc-event-container',true).first();
63475         }
63476         
63477          
63478         this.clearEvent(ev);
63479             //code
63480        
63481         
63482         
63483         ev.els = [];
63484         var cells = ev.cells;
63485         var rows = ev.rows;
63486         this.fireEvent('eventrender', this, ev);
63487         
63488         for(var i =0; i < rows.length; i++) {
63489             
63490             cls = '';
63491             if (i == 0) {
63492                 cls += ' fc-event-start';
63493             }
63494             if ((i+1) == rows.length) {
63495                 cls += ' fc-event-end';
63496             }
63497             
63498             //Roo.log(ev.data);
63499             // how many rows should it span..
63500             var cg = this.eventTmpl.append(ctr,Roo.apply({
63501                 fccls : cls
63502                 
63503             }, ev.data) , true);
63504             
63505             
63506             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63507             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63508             cg.on('click', this.onEventClick, this, ev);
63509             
63510             ev.els.push(cg);
63511             
63512             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63513             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63514             //Roo.log(cg);
63515              
63516             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63517             cg.setWidth(ebox.right - sbox.x -2);
63518         }
63519     },
63520     
63521     renderEvents: function()
63522     {   
63523         // first make sure there is enough space..
63524         
63525         if (!this.eventTmpl) {
63526             this.eventTmpl = new Roo.Template(
63527                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63528                     '<div class="fc-event-inner">' +
63529                         '<span class="fc-event-time">{time}</span>' +
63530                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63531                     '</div>' +
63532                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63533                 '</div>'
63534             );
63535                 
63536         }
63537                
63538         
63539         
63540         this.cells.each(function(c) {
63541             //Roo.log(c.select('.fc-day-content div',true).first());
63542             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63543         });
63544         
63545         var ctr = this.view.el.select('.fc-event-container',true).first();
63546         
63547         var cls;
63548         this.eventStore.each(function(ev){
63549             
63550             this.renderEvent(ev);
63551              
63552              
63553         }, this);
63554         this.view.layout();
63555         
63556     },
63557     
63558     onEventEnter: function (e, el,event,d) {
63559         this.fireEvent('evententer', this, el, event);
63560     },
63561     
63562     onEventLeave: function (e, el,event,d) {
63563         this.fireEvent('eventleave', this, el, event);
63564     },
63565     
63566     onEventClick: function (e, el,event,d) {
63567         this.fireEvent('eventclick', this, el, event);
63568     },
63569     
63570     onMonthChange: function () {
63571         this.store.load();
63572     },
63573     
63574     onLoad: function () {
63575         
63576         //Roo.log('calendar onload');
63577 //         
63578         if(this.eventStore.getCount() > 0){
63579             
63580            
63581             
63582             this.eventStore.each(function(d){
63583                 
63584                 
63585                 // FIXME..
63586                 var add =   d.data;
63587                 if (typeof(add.end_dt) == 'undefined')  {
63588                     Roo.log("Missing End time in calendar data: ");
63589                     Roo.log(d);
63590                     return;
63591                 }
63592                 if (typeof(add.start_dt) == 'undefined')  {
63593                     Roo.log("Missing Start time in calendar data: ");
63594                     Roo.log(d);
63595                     return;
63596                 }
63597                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63598                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63599                 add.id = add.id || d.id;
63600                 add.title = add.title || '??';
63601                 
63602                 this.addItem(d);
63603                 
63604              
63605             },this);
63606         }
63607         
63608         this.renderEvents();
63609     }
63610     
63611
63612 });
63613 /*
63614  grid : {
63615                 xtype: 'Grid',
63616                 xns: Roo.grid,
63617                 listeners : {
63618                     render : function ()
63619                     {
63620                         _this.grid = this;
63621                         
63622                         if (!this.view.el.hasClass('course-timesheet')) {
63623                             this.view.el.addClass('course-timesheet');
63624                         }
63625                         if (this.tsStyle) {
63626                             this.ds.load({});
63627                             return; 
63628                         }
63629                         Roo.log('width');
63630                         Roo.log(_this.grid.view.el.getWidth());
63631                         
63632                         
63633                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63634                             '.course-timesheet .x-grid-row' : {
63635                                 height: '80px'
63636                             },
63637                             '.x-grid-row td' : {
63638                                 'vertical-align' : 0
63639                             },
63640                             '.course-edit-link' : {
63641                                 'color' : 'blue',
63642                                 'text-overflow' : 'ellipsis',
63643                                 'overflow' : 'hidden',
63644                                 'white-space' : 'nowrap',
63645                                 'cursor' : 'pointer'
63646                             },
63647                             '.sub-link' : {
63648                                 'color' : 'green'
63649                             },
63650                             '.de-act-sup-link' : {
63651                                 'color' : 'purple',
63652                                 'text-decoration' : 'line-through'
63653                             },
63654                             '.de-act-link' : {
63655                                 'color' : 'red',
63656                                 'text-decoration' : 'line-through'
63657                             },
63658                             '.course-timesheet .course-highlight' : {
63659                                 'border-top-style': 'dashed !important',
63660                                 'border-bottom-bottom': 'dashed !important'
63661                             },
63662                             '.course-timesheet .course-item' : {
63663                                 'font-family'   : 'tahoma, arial, helvetica',
63664                                 'font-size'     : '11px',
63665                                 'overflow'      : 'hidden',
63666                                 'padding-left'  : '10px',
63667                                 'padding-right' : '10px',
63668                                 'padding-top' : '10px' 
63669                             }
63670                             
63671                         }, Roo.id());
63672                                 this.ds.load({});
63673                     }
63674                 },
63675                 autoWidth : true,
63676                 monitorWindowResize : false,
63677                 cellrenderer : function(v,x,r)
63678                 {
63679                     return v;
63680                 },
63681                 sm : {
63682                     xtype: 'CellSelectionModel',
63683                     xns: Roo.grid
63684                 },
63685                 dataSource : {
63686                     xtype: 'Store',
63687                     xns: Roo.data,
63688                     listeners : {
63689                         beforeload : function (_self, options)
63690                         {
63691                             options.params = options.params || {};
63692                             options.params._month = _this.monthField.getValue();
63693                             options.params.limit = 9999;
63694                             options.params['sort'] = 'when_dt';    
63695                             options.params['dir'] = 'ASC';    
63696                             this.proxy.loadResponse = this.loadResponse;
63697                             Roo.log("load?");
63698                             //this.addColumns();
63699                         },
63700                         load : function (_self, records, options)
63701                         {
63702                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63703                                 // if you click on the translation.. you can edit it...
63704                                 var el = Roo.get(this);
63705                                 var id = el.dom.getAttribute('data-id');
63706                                 var d = el.dom.getAttribute('data-date');
63707                                 var t = el.dom.getAttribute('data-time');
63708                                 //var id = this.child('span').dom.textContent;
63709                                 
63710                                 //Roo.log(this);
63711                                 Pman.Dialog.CourseCalendar.show({
63712                                     id : id,
63713                                     when_d : d,
63714                                     when_t : t,
63715                                     productitem_active : id ? 1 : 0
63716                                 }, function() {
63717                                     _this.grid.ds.load({});
63718                                 });
63719                            
63720                            });
63721                            
63722                            _this.panel.fireEvent('resize', [ '', '' ]);
63723                         }
63724                     },
63725                     loadResponse : function(o, success, response){
63726                             // this is overridden on before load..
63727                             
63728                             Roo.log("our code?");       
63729                             //Roo.log(success);
63730                             //Roo.log(response)
63731                             delete this.activeRequest;
63732                             if(!success){
63733                                 this.fireEvent("loadexception", this, o, response);
63734                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63735                                 return;
63736                             }
63737                             var result;
63738                             try {
63739                                 result = o.reader.read(response);
63740                             }catch(e){
63741                                 Roo.log("load exception?");
63742                                 this.fireEvent("loadexception", this, o, response, e);
63743                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63744                                 return;
63745                             }
63746                             Roo.log("ready...");        
63747                             // loop through result.records;
63748                             // and set this.tdate[date] = [] << array of records..
63749                             _this.tdata  = {};
63750                             Roo.each(result.records, function(r){
63751                                 //Roo.log(r.data);
63752                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63753                                     _this.tdata[r.data.when_dt.format('j')] = [];
63754                                 }
63755                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63756                             });
63757                             
63758                             //Roo.log(_this.tdata);
63759                             
63760                             result.records = [];
63761                             result.totalRecords = 6;
63762                     
63763                             // let's generate some duumy records for the rows.
63764                             //var st = _this.dateField.getValue();
63765                             
63766                             // work out monday..
63767                             //st = st.add(Date.DAY, -1 * st.format('w'));
63768                             
63769                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63770                             
63771                             var firstOfMonth = date.getFirstDayOfMonth();
63772                             var days = date.getDaysInMonth();
63773                             var d = 1;
63774                             var firstAdded = false;
63775                             for (var i = 0; i < result.totalRecords ; i++) {
63776                                 //var d= st.add(Date.DAY, i);
63777                                 var row = {};
63778                                 var added = 0;
63779                                 for(var w = 0 ; w < 7 ; w++){
63780                                     if(!firstAdded && firstOfMonth != w){
63781                                         continue;
63782                                     }
63783                                     if(d > days){
63784                                         continue;
63785                                     }
63786                                     firstAdded = true;
63787                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63788                                     row['weekday'+w] = String.format(
63789                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63790                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63791                                                     d,
63792                                                     date.format('Y-m-')+dd
63793                                                 );
63794                                     added++;
63795                                     if(typeof(_this.tdata[d]) != 'undefined'){
63796                                         Roo.each(_this.tdata[d], function(r){
63797                                             var is_sub = '';
63798                                             var deactive = '';
63799                                             var id = r.id;
63800                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63801                                             if(r.parent_id*1>0){
63802                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63803                                                 id = r.parent_id;
63804                                             }
63805                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63806                                                 deactive = 'de-act-link';
63807                                             }
63808                                             
63809                                             row['weekday'+w] += String.format(
63810                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63811                                                     id, //0
63812                                                     r.product_id_name, //1
63813                                                     r.when_dt.format('h:ia'), //2
63814                                                     is_sub, //3
63815                                                     deactive, //4
63816                                                     desc // 5
63817                                             );
63818                                         });
63819                                     }
63820                                     d++;
63821                                 }
63822                                 
63823                                 // only do this if something added..
63824                                 if(added > 0){ 
63825                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63826                                 }
63827                                 
63828                                 
63829                                 // push it twice. (second one with an hour..
63830                                 
63831                             }
63832                             //Roo.log(result);
63833                             this.fireEvent("load", this, o, o.request.arg);
63834                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63835                         },
63836                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63837                     proxy : {
63838                         xtype: 'HttpProxy',
63839                         xns: Roo.data,
63840                         method : 'GET',
63841                         url : baseURL + '/Roo/Shop_course.php'
63842                     },
63843                     reader : {
63844                         xtype: 'JsonReader',
63845                         xns: Roo.data,
63846                         id : 'id',
63847                         fields : [
63848                             {
63849                                 'name': 'id',
63850                                 'type': 'int'
63851                             },
63852                             {
63853                                 'name': 'when_dt',
63854                                 'type': 'string'
63855                             },
63856                             {
63857                                 'name': 'end_dt',
63858                                 'type': 'string'
63859                             },
63860                             {
63861                                 'name': 'parent_id',
63862                                 'type': 'int'
63863                             },
63864                             {
63865                                 'name': 'product_id',
63866                                 'type': 'int'
63867                             },
63868                             {
63869                                 'name': 'productitem_id',
63870                                 'type': 'int'
63871                             },
63872                             {
63873                                 'name': 'guid',
63874                                 'type': 'int'
63875                             }
63876                         ]
63877                     }
63878                 },
63879                 toolbar : {
63880                     xtype: 'Toolbar',
63881                     xns: Roo,
63882                     items : [
63883                         {
63884                             xtype: 'Button',
63885                             xns: Roo.Toolbar,
63886                             listeners : {
63887                                 click : function (_self, e)
63888                                 {
63889                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63890                                     sd.setMonth(sd.getMonth()-1);
63891                                     _this.monthField.setValue(sd.format('Y-m-d'));
63892                                     _this.grid.ds.load({});
63893                                 }
63894                             },
63895                             text : "Back"
63896                         },
63897                         {
63898                             xtype: 'Separator',
63899                             xns: Roo.Toolbar
63900                         },
63901                         {
63902                             xtype: 'MonthField',
63903                             xns: Roo.form,
63904                             listeners : {
63905                                 render : function (_self)
63906                                 {
63907                                     _this.monthField = _self;
63908                                    // _this.monthField.set  today
63909                                 },
63910                                 select : function (combo, date)
63911                                 {
63912                                     _this.grid.ds.load({});
63913                                 }
63914                             },
63915                             value : (function() { return new Date(); })()
63916                         },
63917                         {
63918                             xtype: 'Separator',
63919                             xns: Roo.Toolbar
63920                         },
63921                         {
63922                             xtype: 'TextItem',
63923                             xns: Roo.Toolbar,
63924                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63925                         },
63926                         {
63927                             xtype: 'Fill',
63928                             xns: Roo.Toolbar
63929                         },
63930                         {
63931                             xtype: 'Button',
63932                             xns: Roo.Toolbar,
63933                             listeners : {
63934                                 click : function (_self, e)
63935                                 {
63936                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63937                                     sd.setMonth(sd.getMonth()+1);
63938                                     _this.monthField.setValue(sd.format('Y-m-d'));
63939                                     _this.grid.ds.load({});
63940                                 }
63941                             },
63942                             text : "Next"
63943                         }
63944                     ]
63945                 },
63946                  
63947             }
63948         };
63949         
63950         *//*
63951  * Based on:
63952  * Ext JS Library 1.1.1
63953  * Copyright(c) 2006-2007, Ext JS, LLC.
63954  *
63955  * Originally Released Under LGPL - original licence link has changed is not relivant.
63956  *
63957  * Fork - LGPL
63958  * <script type="text/javascript">
63959  */
63960  
63961 /**
63962  * @class Roo.LoadMask
63963  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63964  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63965  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63966  * element's UpdateManager load indicator and will be destroyed after the initial load.
63967  * @constructor
63968  * Create a new LoadMask
63969  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63970  * @param {Object} config The config object
63971  */
63972 Roo.LoadMask = function(el, config){
63973     this.el = Roo.get(el);
63974     Roo.apply(this, config);
63975     if(this.store){
63976         this.store.on('beforeload', this.onBeforeLoad, this);
63977         this.store.on('load', this.onLoad, this);
63978         this.store.on('loadexception', this.onLoadException, this);
63979         this.removeMask = false;
63980     }else{
63981         var um = this.el.getUpdateManager();
63982         um.showLoadIndicator = false; // disable the default indicator
63983         um.on('beforeupdate', this.onBeforeLoad, this);
63984         um.on('update', this.onLoad, this);
63985         um.on('failure', this.onLoad, this);
63986         this.removeMask = true;
63987     }
63988 };
63989
63990 Roo.LoadMask.prototype = {
63991     /**
63992      * @cfg {Boolean} removeMask
63993      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
63994      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
63995      */
63996     removeMask : false,
63997     /**
63998      * @cfg {String} msg
63999      * The text to display in a centered loading message box (defaults to 'Loading...')
64000      */
64001     msg : 'Loading...',
64002     /**
64003      * @cfg {String} msgCls
64004      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64005      */
64006     msgCls : 'x-mask-loading',
64007
64008     /**
64009      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64010      * @type Boolean
64011      */
64012     disabled: false,
64013
64014     /**
64015      * Disables the mask to prevent it from being displayed
64016      */
64017     disable : function(){
64018        this.disabled = true;
64019     },
64020
64021     /**
64022      * Enables the mask so that it can be displayed
64023      */
64024     enable : function(){
64025         this.disabled = false;
64026     },
64027     
64028     onLoadException : function()
64029     {
64030         Roo.log(arguments);
64031         
64032         if (typeof(arguments[3]) != 'undefined') {
64033             Roo.MessageBox.alert("Error loading",arguments[3]);
64034         } 
64035         /*
64036         try {
64037             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64038                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64039             }   
64040         } catch(e) {
64041             
64042         }
64043         */
64044     
64045         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64046     },
64047     // private
64048     onLoad : function()
64049     {
64050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64051     },
64052
64053     // private
64054     onBeforeLoad : function(){
64055         if(!this.disabled){
64056             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64057         }
64058     },
64059
64060     // private
64061     destroy : function(){
64062         if(this.store){
64063             this.store.un('beforeload', this.onBeforeLoad, this);
64064             this.store.un('load', this.onLoad, this);
64065             this.store.un('loadexception', this.onLoadException, this);
64066         }else{
64067             var um = this.el.getUpdateManager();
64068             um.un('beforeupdate', this.onBeforeLoad, this);
64069             um.un('update', this.onLoad, this);
64070             um.un('failure', this.onLoad, this);
64071         }
64072     }
64073 };/*
64074  * Based on:
64075  * Ext JS Library 1.1.1
64076  * Copyright(c) 2006-2007, Ext JS, LLC.
64077  *
64078  * Originally Released Under LGPL - original licence link has changed is not relivant.
64079  *
64080  * Fork - LGPL
64081  * <script type="text/javascript">
64082  */
64083
64084
64085 /**
64086  * @class Roo.XTemplate
64087  * @extends Roo.Template
64088  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64089 <pre><code>
64090 var t = new Roo.XTemplate(
64091         '&lt;select name="{name}"&gt;',
64092                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64093         '&lt;/select&gt;'
64094 );
64095  
64096 // then append, applying the master template values
64097  </code></pre>
64098  *
64099  * Supported features:
64100  *
64101  *  Tags:
64102
64103 <pre><code>
64104       {a_variable} - output encoded.
64105       {a_variable.format:("Y-m-d")} - call a method on the variable
64106       {a_variable:raw} - unencoded output
64107       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64108       {a_variable:this.method_on_template(...)} - call a method on the template object.
64109  
64110 </code></pre>
64111  *  The tpl tag:
64112 <pre><code>
64113         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64114         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64115         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64116         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64117   
64118         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64119         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64120 </code></pre>
64121  *      
64122  */
64123 Roo.XTemplate = function()
64124 {
64125     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64126     if (this.html) {
64127         this.compile();
64128     }
64129 };
64130
64131
64132 Roo.extend(Roo.XTemplate, Roo.Template, {
64133
64134     /**
64135      * The various sub templates
64136      */
64137     tpls : false,
64138     /**
64139      *
64140      * basic tag replacing syntax
64141      * WORD:WORD()
64142      *
64143      * // you can fake an object call by doing this
64144      *  x.t:(test,tesT) 
64145      * 
64146      */
64147     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64148
64149     /**
64150      * compile the template
64151      *
64152      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64153      *
64154      */
64155     compile: function()
64156     {
64157         var s = this.html;
64158      
64159         s = ['<tpl>', s, '</tpl>'].join('');
64160     
64161         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64162             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64163             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64164             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64165             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64166             m,
64167             id     = 0,
64168             tpls   = [];
64169     
64170         while(true == !!(m = s.match(re))){
64171             var forMatch   = m[0].match(nameRe),
64172                 ifMatch   = m[0].match(ifRe),
64173                 execMatch   = m[0].match(execRe),
64174                 namedMatch   = m[0].match(namedRe),
64175                 
64176                 exp  = null, 
64177                 fn   = null,
64178                 exec = null,
64179                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64180                 
64181             if (ifMatch) {
64182                 // if - puts fn into test..
64183                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64184                 if(exp){
64185                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64186                 }
64187             }
64188             
64189             if (execMatch) {
64190                 // exec - calls a function... returns empty if true is  returned.
64191                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64192                 if(exp){
64193                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64194                 }
64195             }
64196             
64197             
64198             if (name) {
64199                 // for = 
64200                 switch(name){
64201                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64202                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64203                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64204                 }
64205             }
64206             var uid = namedMatch ? namedMatch[1] : id;
64207             
64208             
64209             tpls.push({
64210                 id:     namedMatch ? namedMatch[1] : id,
64211                 target: name,
64212                 exec:   exec,
64213                 test:   fn,
64214                 body:   m[1] || ''
64215             });
64216             if (namedMatch) {
64217                 s = s.replace(m[0], '');
64218             } else { 
64219                 s = s.replace(m[0], '{xtpl'+ id + '}');
64220             }
64221             ++id;
64222         }
64223         this.tpls = [];
64224         for(var i = tpls.length-1; i >= 0; --i){
64225             this.compileTpl(tpls[i]);
64226             this.tpls[tpls[i].id] = tpls[i];
64227         }
64228         this.master = tpls[tpls.length-1];
64229         return this;
64230     },
64231     /**
64232      * same as applyTemplate, except it's done to one of the subTemplates
64233      * when using named templates, you can do:
64234      *
64235      * var str = pl.applySubTemplate('your-name', values);
64236      *
64237      * 
64238      * @param {Number} id of the template
64239      * @param {Object} values to apply to template
64240      * @param {Object} parent (normaly the instance of this object)
64241      */
64242     applySubTemplate : function(id, values, parent)
64243     {
64244         
64245         
64246         var t = this.tpls[id];
64247         
64248         
64249         try { 
64250             if(t.test && !t.test.call(this, values, parent)){
64251                 return '';
64252             }
64253         } catch(e) {
64254             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64255             Roo.log(e.toString());
64256             Roo.log(t.test);
64257             return ''
64258         }
64259         try { 
64260             
64261             if(t.exec && t.exec.call(this, values, parent)){
64262                 return '';
64263             }
64264         } catch(e) {
64265             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64266             Roo.log(e.toString());
64267             Roo.log(t.exec);
64268             return ''
64269         }
64270         try {
64271             var vs = t.target ? t.target.call(this, values, parent) : values;
64272             parent = t.target ? values : parent;
64273             if(t.target && vs instanceof Array){
64274                 var buf = [];
64275                 for(var i = 0, len = vs.length; i < len; i++){
64276                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64277                 }
64278                 return buf.join('');
64279             }
64280             return t.compiled.call(this, vs, parent);
64281         } catch (e) {
64282             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64283             Roo.log(e.toString());
64284             Roo.log(t.compiled);
64285             return '';
64286         }
64287     },
64288
64289     compileTpl : function(tpl)
64290     {
64291         var fm = Roo.util.Format;
64292         var useF = this.disableFormats !== true;
64293         var sep = Roo.isGecko ? "+" : ",";
64294         var undef = function(str) {
64295             Roo.log("Property not found :"  + str);
64296             return '';
64297         };
64298         
64299         var fn = function(m, name, format, args)
64300         {
64301             //Roo.log(arguments);
64302             args = args ? args.replace(/\\'/g,"'") : args;
64303             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64304             if (typeof(format) == 'undefined') {
64305                 format= 'htmlEncode';
64306             }
64307             if (format == 'raw' ) {
64308                 format = false;
64309             }
64310             
64311             if(name.substr(0, 4) == 'xtpl'){
64312                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64313             }
64314             
64315             // build an array of options to determine if value is undefined..
64316             
64317             // basically get 'xxxx.yyyy' then do
64318             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64319             //    (function () { Roo.log("Property not found"); return ''; })() :
64320             //    ......
64321             
64322             var udef_ar = [];
64323             var lookfor = '';
64324             Roo.each(name.split('.'), function(st) {
64325                 lookfor += (lookfor.length ? '.': '') + st;
64326                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64327             });
64328             
64329             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64330             
64331             
64332             if(format && useF){
64333                 
64334                 args = args ? ',' + args : "";
64335                  
64336                 if(format.substr(0, 5) != "this."){
64337                     format = "fm." + format + '(';
64338                 }else{
64339                     format = 'this.call("'+ format.substr(5) + '", ';
64340                     args = ", values";
64341                 }
64342                 
64343                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64344             }
64345              
64346             if (args.length) {
64347                 // called with xxyx.yuu:(test,test)
64348                 // change to ()
64349                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64350             }
64351             // raw.. - :raw modifier..
64352             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64353             
64354         };
64355         var body;
64356         // branched to use + in gecko and [].join() in others
64357         if(Roo.isGecko){
64358             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64359                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64360                     "';};};";
64361         }else{
64362             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64363             body.push(tpl.body.replace(/(\r\n|\n)/g,
64364                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64365             body.push("'].join('');};};");
64366             body = body.join('');
64367         }
64368         
64369         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64370        
64371         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64372         eval(body);
64373         
64374         return this;
64375     },
64376
64377     applyTemplate : function(values){
64378         return this.master.compiled.call(this, values, {});
64379         //var s = this.subs;
64380     },
64381
64382     apply : function(){
64383         return this.applyTemplate.apply(this, arguments);
64384     }
64385
64386  });
64387
64388 Roo.XTemplate.from = function(el){
64389     el = Roo.getDom(el);
64390     return new Roo.XTemplate(el.value || el.innerHTML);
64391 };