roojs-core.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 /**
959   * Make the first letter of a string uppercase
960   *
961   * @return {String} The new string.
962   */
963 String.prototype.toUpperCaseFirst = function () {
964     return this.charAt(0).toUpperCase() + this.slice(1);
965 };  
966   
967 /*
968  * Based on:
969  * Ext JS Library 1.1.1
970  * Copyright(c) 2006-2007, Ext JS, LLC.
971  *
972  * Originally Released Under LGPL - original licence link has changed is not relivant.
973  *
974  * Fork - LGPL
975  * <script type="text/javascript">
976  */
977
978  /**
979  * @class Number
980  */
981 Roo.applyIf(Number.prototype, {
982     /**
983      * Checks whether or not the current number is within a desired range.  If the number is already within the
984      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985      * exceeded.  Note that this method returns the constrained value but does not change the current number.
986      * @param {Number} min The minimum number in the range
987      * @param {Number} max The maximum number in the range
988      * @return {Number} The constrained value if outside the range, otherwise the current value
989      */
990     constrain : function(min, max){
991         return Math.min(Math.max(this, min), max);
992     }
993 });/*
994  * Based on:
995  * Ext JS Library 1.1.1
996  * Copyright(c) 2006-2007, Ext JS, LLC.
997  *
998  * Originally Released Under LGPL - original licence link has changed is not relivant.
999  *
1000  * Fork - LGPL
1001  * <script type="text/javascript">
1002  */
1003  /**
1004  * @class Array
1005  */
1006 Roo.applyIf(Array.prototype, {
1007     /**
1008      * 
1009      * Checks whether or not the specified object exists in the array.
1010      * @param {Object} o The object to check for
1011      * @return {Number} The index of o in the array (or -1 if it is not found)
1012      */
1013     indexOf : function(o){
1014        for (var i = 0, len = this.length; i < len; i++){
1015               if(this[i] == o) { return i; }
1016        }
1017            return -1;
1018     },
1019
1020     /**
1021      * Removes the specified object from the array.  If the object is not found nothing happens.
1022      * @param {Object} o The object to remove
1023      */
1024     remove : function(o){
1025        var index = this.indexOf(o);
1026        if(index != -1){
1027            this.splice(index, 1);
1028        }
1029     },
1030     /**
1031      * Map (JS 1.6 compatibility)
1032      * @param {Function} function  to call
1033      */
1034     map : function(fun )
1035     {
1036         var len = this.length >>> 0;
1037         if (typeof fun != "function") {
1038             throw new TypeError();
1039         }
1040         var res = new Array(len);
1041         var thisp = arguments[1];
1042         for (var i = 0; i < len; i++)
1043         {
1044             if (i in this) {
1045                 res[i] = fun.call(thisp, this[i], i, this);
1046             }
1047         }
1048
1049         return res;
1050     },
1051     /**
1052      * equals
1053      * @param {Array} o The array to compare to
1054      * @returns {Boolean} true if the same
1055      */
1056     equals : function(b)
1057     {
1058             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1059         if (this === b) {
1060             return true;
1061         }
1062         if (b == null) {
1063             return false;
1064         }
1065         if (this.length !== b.length) {
1066             return false;
1067         }
1068           
1069         // sort?? a.sort().equals(b.sort());
1070           
1071         for (var i = 0; i < this.length; ++i) {
1072             if (this[i] !== b[i]) {
1073             return false;
1074             }
1075         }
1076         return true;
1077     } 
1078     
1079     
1080     
1081     
1082 });
1083
1084 Roo.applyIf(Array, {
1085  /**
1086      * from
1087      * @static
1088      * @param {Array} o Or Array like object (eg. nodelist)
1089      * @returns {Array} 
1090      */
1091     from : function(o)
1092     {
1093         var ret= [];
1094     
1095         for (var i =0; i < o.length; i++) { 
1096             ret[i] = o[i];
1097         }
1098         return ret;
1099       
1100     }
1101 });
1102 /*
1103  * Based on:
1104  * Ext JS Library 1.1.1
1105  * Copyright(c) 2006-2007, Ext JS, LLC.
1106  *
1107  * Originally Released Under LGPL - original licence link has changed is not relivant.
1108  *
1109  * Fork - LGPL
1110  * <script type="text/javascript">
1111  */
1112
1113 /**
1114  * @class Date
1115  *
1116  * The date parsing and format syntax is a subset of
1117  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118  * supported will provide results equivalent to their PHP versions.
1119  *
1120  * Following is the list of all currently supported formats:
1121  *<pre>
1122 Sample date:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1124
1125 Format  Output      Description
1126 ------  ----------  --------------------------------------------------------------
1127   d      10         Day of the month, 2 digits with leading zeros
1128   D      Wed        A textual representation of a day, three letters
1129   j      10         Day of the month without leading zeros
1130   l      Wednesday  A full textual representation of the day of the week
1131   S      th         English ordinal day of month suffix, 2 chars (use with j)
1132   w      3          Numeric representation of the day of the week
1133   z      9          The julian date, or day of the year (0-365)
1134   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135   F      January    A full textual representation of the month
1136   m      01         Numeric representation of a month, with leading zeros
1137   M      Jan        Month name abbreviation, three letters
1138   n      1          Numeric representation of a month, without leading zeros
1139   t      31         Number of days in the given month
1140   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1141   Y      2007       A full numeric representation of a year, 4 digits
1142   y      07         A two digit representation of a year
1143   a      pm         Lowercase Ante meridiem and Post meridiem
1144   A      PM         Uppercase Ante meridiem and Post meridiem
1145   g      3          12-hour format of an hour without leading zeros
1146   G      15         24-hour format of an hour without leading zeros
1147   h      03         12-hour format of an hour with leading zeros
1148   H      15         24-hour format of an hour with leading zeros
1149   i      05         Minutes with leading zeros
1150   s      01         Seconds, with leading zeros
1151   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1153   T      CST        Timezone setting of the machine running the code
1154   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1155 </pre>
1156  *
1157  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1158  * <pre><code>
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d'));                         //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1162 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
1163  </code></pre>
1164  *
1165  * Here are some standard date/time patterns that you might find helpful.  They
1166  * are not part of the source of Date.js, but to use them you can simply copy this
1167  * block of code into any script that is included after Date.js and they will also become
1168  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1169  * <pre><code>
1170 Date.patterns = {
1171     ISO8601Long:"Y-m-d H:i:s",
1172     ISO8601Short:"Y-m-d",
1173     ShortDate: "n/j/Y",
1174     LongDate: "l, F d, Y",
1175     FullDateTime: "l, F d, Y g:i:s A",
1176     MonthDay: "F d",
1177     ShortTime: "g:i A",
1178     LongTime: "g:i:s A",
1179     SortableDateTime: "Y-m-d\\TH:i:s",
1180     UniversalSortableDateTime: "Y-m-d H:i:sO",
1181     YearMonth: "F, Y"
1182 };
1183 </code></pre>
1184  *
1185  * Example usage:
1186  * <pre><code>
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1189  </code></pre>
1190  */
1191
1192 /*
1193  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194  * They generate precompiled functions from date formats instead of parsing and
1195  * processing the pattern every time you format a date.  These functions are available
1196  * on every Date object (any javascript function).
1197  *
1198  * The original article and download are here:
1199  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1200  *
1201  */
1202  
1203  
1204  // was in core
1205 /**
1206  Returns the number of milliseconds between this date and date
1207  @param {Date} date (optional) Defaults to now
1208  @param {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY) 
1209  @return {Number} The diff in milliseconds or units of interval
1210  @member Date getElapsed
1211  */
1212 Date.prototype.getElapsed = function(date, interval)
1213 {
1214     date = date ||  new Date();
1215     var ret = Math.abs(date.getTime()-this.getTime());
1216     switch (interval) {
1217        
1218         case  Date.SECOND:
1219             return Math.floor(ret / (1000));
1220         case  Date.MINUTE:
1221             return Math.floor(ret / (1000*60));
1222         case  Date.HOUR:
1223             return Math.floor(ret / (1000*60*60));
1224         case  Date.DAY:
1225             return Math.floor(ret / (1000*60*60*24));
1226         case  Date.MONTH: // this does not give exact number...??
1227             return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1228         case  Date.YEAR: // this does not give exact number...??
1229             return (date.format("Y") - this.format("Y"));
1230        
1231         case  Date.MILLI:
1232         default:
1233             return ret;
1234     }
1235 };
1236  
1237 // was in date file..
1238
1239
1240 // private
1241 Date.parseFunctions = {count:0};
1242 // private
1243 Date.parseRegexes = [];
1244 // private
1245 Date.formatFunctions = {count:0};
1246
1247 // private
1248 Date.prototype.dateFormat = function(format) {
1249     if (Date.formatFunctions[format] == null) {
1250         Date.createNewFormat(format);
1251     }
1252     var func = Date.formatFunctions[format];
1253     return this[func]();
1254 };
1255
1256
1257 /**
1258  * Formats a date given the supplied format string
1259  * @param {String} format The format string
1260  * @return {String} The formatted date
1261  * @method
1262  */
1263 Date.prototype.format = Date.prototype.dateFormat;
1264
1265 // private
1266 Date.createNewFormat = function(format) {
1267     var funcName = "format" + Date.formatFunctions.count++;
1268     Date.formatFunctions[format] = funcName;
1269     var code = "Date.prototype." + funcName + " = function(){return ";
1270     var special = false;
1271     var ch = '';
1272     for (var i = 0; i < format.length; ++i) {
1273         ch = format.charAt(i);
1274         if (!special && ch == "\\") {
1275             special = true;
1276         }
1277         else if (special) {
1278             special = false;
1279             code += "'" + String.escape(ch) + "' + ";
1280         }
1281         else {
1282             code += Date.getFormatCode(ch);
1283         }
1284     }
1285     /** eval:var:zzzzzzzzzzzzz */
1286     eval(code.substring(0, code.length - 3) + ";}");
1287 };
1288
1289 // private
1290 Date.getFormatCode = function(character) {
1291     switch (character) {
1292     case "d":
1293         return "String.leftPad(this.getDate(), 2, '0') + ";
1294     case "D":
1295         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1296     case "j":
1297         return "this.getDate() + ";
1298     case "l":
1299         return "Date.dayNames[this.getDay()] + ";
1300     case "S":
1301         return "this.getSuffix() + ";
1302     case "w":
1303         return "this.getDay() + ";
1304     case "z":
1305         return "this.getDayOfYear() + ";
1306     case "W":
1307         return "this.getWeekOfYear() + ";
1308     case "F":
1309         return "Date.monthNames[this.getMonth()] + ";
1310     case "m":
1311         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1312     case "M":
1313         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1314     case "n":
1315         return "(this.getMonth() + 1) + ";
1316     case "t":
1317         return "this.getDaysInMonth() + ";
1318     case "L":
1319         return "(this.isLeapYear() ? 1 : 0) + ";
1320     case "Y":
1321         return "this.getFullYear() + ";
1322     case "y":
1323         return "('' + this.getFullYear()).substring(2, 4) + ";
1324     case "a":
1325         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1326     case "A":
1327         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1328     case "g":
1329         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1330     case "G":
1331         return "this.getHours() + ";
1332     case "h":
1333         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1334     case "H":
1335         return "String.leftPad(this.getHours(), 2, '0') + ";
1336     case "i":
1337         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1338     case "s":
1339         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1340     case "O":
1341         return "this.getGMTOffset() + ";
1342     case "P":
1343         return "this.getGMTColonOffset() + ";
1344     case "T":
1345         return "this.getTimezone() + ";
1346     case "Z":
1347         return "(this.getTimezoneOffset() * -60) + ";
1348     default:
1349         return "'" + String.escape(character) + "' + ";
1350     }
1351 };
1352
1353 /**
1354  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1355  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1356  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1357  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1358  * string or the parse operation will fail.
1359  * Example Usage:
1360 <pre><code>
1361 //dt = Fri May 25 2007 (current date)
1362 var dt = new Date();
1363
1364 //dt = Thu May 25 2006 (today's month/day in 2006)
1365 dt = Date.parseDate("2006", "Y");
1366
1367 //dt = Sun Jan 15 2006 (all date parts specified)
1368 dt = Date.parseDate("2006-1-15", "Y-m-d");
1369
1370 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1371 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1372 </code></pre>
1373  * @param {String} input The unparsed date as a string
1374  * @param {String} format The format the date is in
1375  * @return {Date} The parsed date
1376  * @static
1377  */
1378 Date.parseDate = function(input, format) {
1379     if (Date.parseFunctions[format] == null) {
1380         Date.createParser(format);
1381     }
1382     var func = Date.parseFunctions[format];
1383     return Date[func](input);
1384 };
1385 /**
1386  * @private
1387  */
1388
1389 Date.createParser = function(format) {
1390     var funcName = "parse" + Date.parseFunctions.count++;
1391     var regexNum = Date.parseRegexes.length;
1392     var currentGroup = 1;
1393     Date.parseFunctions[format] = funcName;
1394
1395     var code = "Date." + funcName + " = function(input){\n"
1396         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1397         + "var d = new Date();\n"
1398         + "y = d.getFullYear();\n"
1399         + "m = d.getMonth();\n"
1400         + "d = d.getDate();\n"
1401         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1402         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1403         + "if (results && results.length > 0) {";
1404     var regex = "";
1405
1406     var special = false;
1407     var ch = '';
1408     for (var i = 0; i < format.length; ++i) {
1409         ch = format.charAt(i);
1410         if (!special && ch == "\\") {
1411             special = true;
1412         }
1413         else if (special) {
1414             special = false;
1415             regex += String.escape(ch);
1416         }
1417         else {
1418             var obj = Date.formatCodeToRegex(ch, currentGroup);
1419             currentGroup += obj.g;
1420             regex += obj.s;
1421             if (obj.g && obj.c) {
1422                 code += obj.c;
1423             }
1424         }
1425     }
1426
1427     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1428         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1429         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1430         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1431         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1432         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1433         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1434         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1435         + "else if (y >= 0 && m >= 0)\n"
1436         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1437         + "else if (y >= 0)\n"
1438         + "{v = new Date(y); v.setFullYear(y);}\n"
1439         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1440         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1441         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1442         + ";}";
1443
1444     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1445     /** eval:var:zzzzzzzzzzzzz */
1446     eval(code);
1447 };
1448
1449 // private
1450 Date.formatCodeToRegex = function(character, currentGroup) {
1451     switch (character) {
1452     case "D":
1453         return {g:0,
1454         c:null,
1455         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1456     case "j":
1457         return {g:1,
1458             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1459             s:"(\\d{1,2})"}; // day of month without leading zeroes
1460     case "d":
1461         return {g:1,
1462             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; // day of month with leading zeroes
1464     case "l":
1465         return {g:0,
1466             c:null,
1467             s:"(?:" + Date.dayNames.join("|") + ")"};
1468     case "S":
1469         return {g:0,
1470             c:null,
1471             s:"(?:st|nd|rd|th)"};
1472     case "w":
1473         return {g:0,
1474             c:null,
1475             s:"\\d"};
1476     case "z":
1477         return {g:0,
1478             c:null,
1479             s:"(?:\\d{1,3})"};
1480     case "W":
1481         return {g:0,
1482             c:null,
1483             s:"(?:\\d{2})"};
1484     case "F":
1485         return {g:1,
1486             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1487             s:"(" + Date.monthNames.join("|") + ")"};
1488     case "M":
1489         return {g:1,
1490             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1491             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1492     case "n":
1493         return {g:1,
1494             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1495             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1496     case "m":
1497         return {g:1,
1498             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1499             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1500     case "t":
1501         return {g:0,
1502             c:null,
1503             s:"\\d{1,2}"};
1504     case "L":
1505         return {g:0,
1506             c:null,
1507             s:"(?:1|0)"};
1508     case "Y":
1509         return {g:1,
1510             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1511             s:"(\\d{4})"};
1512     case "y":
1513         return {g:1,
1514             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1515                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1516             s:"(\\d{1,2})"};
1517     case "a":
1518         return {g:1,
1519             c:"if (results[" + currentGroup + "] == 'am') {\n"
1520                 + "if (h == 12) { h = 0; }\n"
1521                 + "} else { if (h < 12) { h += 12; }}",
1522             s:"(am|pm)"};
1523     case "A":
1524         return {g:1,
1525             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1526                 + "if (h == 12) { h = 0; }\n"
1527                 + "} else { if (h < 12) { h += 12; }}",
1528             s:"(AM|PM)"};
1529     case "g":
1530     case "G":
1531         return {g:1,
1532             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1533             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1534     case "h":
1535     case "H":
1536         return {g:1,
1537             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1538             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1539     case "i":
1540         return {g:1,
1541             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1542             s:"(\\d{2})"};
1543     case "s":
1544         return {g:1,
1545             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1546             s:"(\\d{2})"};
1547     case "O":
1548         return {g:1,
1549             c:[
1550                 "o = results[", currentGroup, "];\n",
1551                 "var sn = o.substring(0,1);\n", // get + / - sign
1552                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1553                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1554                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1555                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1556             ].join(""),
1557             s:"([+\-]\\d{2,4})"};
1558     
1559     
1560     case "P":
1561         return {g:1,
1562                 c:[
1563                    "o = results[", currentGroup, "];\n",
1564                    "var sn = o.substring(0,1);\n",
1565                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1566                    "var mn = o.substring(4,6) % 60;\n",
1567                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1568                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1569             ].join(""),
1570             s:"([+\-]\\d{4})"};
1571     case "T":
1572         return {g:0,
1573             c:null,
1574             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1575     case "Z":
1576         return {g:1,
1577             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1578                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1579             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1580     default:
1581         return {g:0,
1582             c:null,
1583             s:String.escape(character)};
1584     }
1585 };
1586
1587 /**
1588  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1589  * @return {String} The abbreviated timezone name (e.g. 'CST')
1590  */
1591 Date.prototype.getTimezone = function() {
1592     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1593 };
1594
1595 /**
1596  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1597  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1598  */
1599 Date.prototype.getGMTOffset = function() {
1600     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1601         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1602         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1603 };
1604
1605 /**
1606  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1607  * @return {String} 2-characters representing hours and 2-characters representing minutes
1608  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1609  */
1610 Date.prototype.getGMTColonOffset = function() {
1611         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1612                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1613                 + ":"
1614                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1615 }
1616
1617 /**
1618  * Get the numeric day number of the year, adjusted for leap year.
1619  * @return {Number} 0 through 364 (365 in leap years)
1620  */
1621 Date.prototype.getDayOfYear = function() {
1622     var num = 0;
1623     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1624     for (var i = 0; i < this.getMonth(); ++i) {
1625         num += Date.daysInMonth[i];
1626     }
1627     return num + this.getDate() - 1;
1628 };
1629
1630 /**
1631  * Get the string representation of the numeric week number of the year
1632  * (equivalent to the format specifier 'W').
1633  * @return {String} '00' through '52'
1634  */
1635 Date.prototype.getWeekOfYear = function() {
1636     // Skip to Thursday of this week
1637     var now = this.getDayOfYear() + (4 - this.getDay());
1638     // Find the first Thursday of the year
1639     var jan1 = new Date(this.getFullYear(), 0, 1);
1640     var then = (7 - jan1.getDay() + 4);
1641     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1642 };
1643
1644 /**
1645  * Whether or not the current date is in a leap year.
1646  * @return {Boolean} True if the current date is in a leap year, else false
1647  */
1648 Date.prototype.isLeapYear = function() {
1649     var year = this.getFullYear();
1650     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1651 };
1652
1653 /**
1654  * Get the first day of the current month, adjusted for leap year.  The returned value
1655  * is the numeric day index within the week (0-6) which can be used in conjunction with
1656  * the {@link #monthNames} array to retrieve the textual day name.
1657  * Example:
1658  *<pre><code>
1659 var dt = new Date('1/10/2007');
1660 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1661 </code></pre>
1662  * @return {Number} The day number (0-6)
1663  */
1664 Date.prototype.getFirstDayOfMonth = function() {
1665     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1666     return (day < 0) ? (day + 7) : day;
1667 };
1668
1669 /**
1670  * Get the last day of the current month, adjusted for leap year.  The returned value
1671  * is the numeric day index within the week (0-6) which can be used in conjunction with
1672  * the {@link #monthNames} array to retrieve the textual day name.
1673  * Example:
1674  *<pre><code>
1675 var dt = new Date('1/10/2007');
1676 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1677 </code></pre>
1678  * @return {Number} The day number (0-6)
1679  */
1680 Date.prototype.getLastDayOfMonth = function() {
1681     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1682     return (day < 0) ? (day + 7) : day;
1683 };
1684
1685
1686 /**
1687  * Get the first date of this date's month
1688  * @return {Date}
1689  */
1690 Date.prototype.getFirstDateOfMonth = function() {
1691     return new Date(this.getFullYear(), this.getMonth(), 1);
1692 };
1693
1694 /**
1695  * Get the last date of this date's month
1696  * @return {Date}
1697  */
1698 Date.prototype.getLastDateOfMonth = function() {
1699     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1700 };
1701 /**
1702  * Get the number of days in the current month, adjusted for leap year.
1703  * @return {Number} The number of days in the month
1704  */
1705 Date.prototype.getDaysInMonth = function() {
1706     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1707     return Date.daysInMonth[this.getMonth()];
1708 };
1709
1710 /**
1711  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1712  * @return {String} 'st, 'nd', 'rd' or 'th'
1713  */
1714 Date.prototype.getSuffix = function() {
1715     switch (this.getDate()) {
1716         case 1:
1717         case 21:
1718         case 31:
1719             return "st";
1720         case 2:
1721         case 22:
1722             return "nd";
1723         case 3:
1724         case 23:
1725             return "rd";
1726         default:
1727             return "th";
1728     }
1729 };
1730
1731 // private
1732 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1733
1734 /**
1735  * An array of textual month names.
1736  * Override these values for international dates, for example...
1737  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1738  * @type Array
1739  * @static
1740  */
1741 Date.monthNames =
1742    ["January",
1743     "February",
1744     "March",
1745     "April",
1746     "May",
1747     "June",
1748     "July",
1749     "August",
1750     "September",
1751     "October",
1752     "November",
1753     "December"];
1754
1755 /**
1756  * An array of textual day names.
1757  * Override these values for international dates, for example...
1758  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1759  * @type Array
1760  * @static
1761  */
1762 Date.dayNames =
1763    ["Sunday",
1764     "Monday",
1765     "Tuesday",
1766     "Wednesday",
1767     "Thursday",
1768     "Friday",
1769     "Saturday"];
1770
1771 // private
1772 Date.y2kYear = 50;
1773 // private
1774 Date.monthNumbers = {
1775     Jan:0,
1776     Feb:1,
1777     Mar:2,
1778     Apr:3,
1779     May:4,
1780     Jun:5,
1781     Jul:6,
1782     Aug:7,
1783     Sep:8,
1784     Oct:9,
1785     Nov:10,
1786     Dec:11};
1787
1788 /**
1789  * Creates and returns a new Date instance with the exact same date value as the called instance.
1790  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1791  * variable will also be changed.  When the intention is to create a new variable that will not
1792  * modify the original instance, you should create a clone.
1793  *
1794  * Example of correctly cloning a date:
1795  * <pre><code>
1796 //wrong way:
1797 var orig = new Date('10/1/2006');
1798 var copy = orig;
1799 copy.setDate(5);
1800 document.write(orig);  //returns 'Thu Oct 05 2006'!
1801
1802 //correct way:
1803 var orig = new Date('10/1/2006');
1804 var copy = orig.clone();
1805 copy.setDate(5);
1806 document.write(orig);  //returns 'Thu Oct 01 2006'
1807 </code></pre>
1808  * @return {Date} The new Date instance
1809  */
1810 Date.prototype.clone = function() {
1811         return new Date(this.getTime());
1812 };
1813
1814 /**
1815  * Clears any time information from this date
1816  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1817  @return {Date} this or the clone
1818  */
1819 Date.prototype.clearTime = function(clone){
1820     if(clone){
1821         return this.clone().clearTime();
1822     }
1823     this.setHours(0);
1824     this.setMinutes(0);
1825     this.setSeconds(0);
1826     this.setMilliseconds(0);
1827     return this;
1828 };
1829
1830 // private
1831 // safari setMonth is broken -- check that this is only donw once...
1832 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1833     Date.brokenSetMonth = Date.prototype.setMonth;
1834         Date.prototype.setMonth = function(num){
1835                 if(num <= -1){
1836                         var n = Math.ceil(-num);
1837                         var back_year = Math.ceil(n/12);
1838                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1839                         this.setFullYear(this.getFullYear() - back_year);
1840                         return Date.brokenSetMonth.call(this, month);
1841                 } else {
1842                         return Date.brokenSetMonth.apply(this, arguments);
1843                 }
1844         };
1845 }
1846
1847 /** Date interval constant 
1848 * @static 
1849 * @type String */
1850 Date.MILLI = "ms";
1851 /** Date interval constant 
1852 * @static 
1853 * @type String */
1854 Date.SECOND = "s";
1855 /** Date interval constant 
1856 * @static 
1857 * @type String */
1858 Date.MINUTE = "mi";
1859 /** Date interval constant 
1860 * @static 
1861 * @type String */
1862 Date.HOUR = "h";
1863 /** Date interval constant 
1864 * @static 
1865 * @type String */
1866 Date.DAY = "d";
1867 /** Date interval constant 
1868 * @static 
1869 * @type String */
1870 Date.MONTH = "mo";
1871 /** Date interval constant 
1872 * @static 
1873 * @type String */
1874 Date.YEAR = "y";
1875
1876 /**
1877  * Provides a convenient method of performing basic date arithmetic.  This method
1878  * does not modify the Date instance being called - it creates and returns
1879  * a new Date instance containing the resulting date value.
1880  *
1881  * Examples:
1882  * <pre><code>
1883 //Basic usage:
1884 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1885 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1886
1887 //Negative values will subtract correctly:
1888 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1889 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1890
1891 //You can even chain several calls together in one line!
1892 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1893 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1894  </code></pre>
1895  *
1896  * @param {String} interval   A valid date interval enum value
1897  * @param {Number} value      The amount to add to the current date
1898  * @return {Date} The new Date instance
1899  */
1900 Date.prototype.add = function(interval, value){
1901   var d = this.clone();
1902   if (!interval || value === 0) { return d; }
1903   switch(interval.toLowerCase()){
1904     case Date.MILLI:
1905       d.setMilliseconds(this.getMilliseconds() + value);
1906       break;
1907     case Date.SECOND:
1908       d.setSeconds(this.getSeconds() + value);
1909       break;
1910     case Date.MINUTE:
1911       d.setMinutes(this.getMinutes() + value);
1912       break;
1913     case Date.HOUR:
1914       d.setHours(this.getHours() + value);
1915       break;
1916     case Date.DAY:
1917       d.setDate(this.getDate() + value);
1918       break;
1919     case Date.MONTH:
1920       var day = this.getDate();
1921       if(day > 28){
1922           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1923       }
1924       d.setDate(day);
1925       d.setMonth(this.getMonth() + value);
1926       break;
1927     case Date.YEAR:
1928       d.setFullYear(this.getFullYear() + value);
1929       break;
1930   }
1931   return d;
1932 };
1933 /**
1934  * @class Roo.lib.Dom
1935  * @licence LGPL
1936  * @static
1937  * 
1938  * Dom utils (from YIU afaik)
1939  *
1940  * 
1941  **/
1942 Roo.lib.Dom = {
1943     /**
1944      * Get the view width
1945      * @param {Boolean} full True will get the full document, otherwise it's the view width
1946      * @return {Number} The width
1947      */
1948      
1949     getViewWidth : function(full) {
1950         return full ? this.getDocumentWidth() : this.getViewportWidth();
1951     },
1952     /**
1953      * Get the view height
1954      * @param {Boolean} full True will get the full document, otherwise it's the view height
1955      * @return {Number} The height
1956      */
1957     getViewHeight : function(full) {
1958         return full ? this.getDocumentHeight() : this.getViewportHeight();
1959     },
1960     /**
1961      * Get the Full Document height 
1962      * @return {Number} The height
1963      */
1964     getDocumentHeight: function() {
1965         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1966         return Math.max(scrollHeight, this.getViewportHeight());
1967     },
1968     /**
1969      * Get the Full Document width
1970      * @return {Number} The width
1971      */
1972     getDocumentWidth: function() {
1973         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1974         return Math.max(scrollWidth, this.getViewportWidth());
1975     },
1976     /**
1977      * Get the Window Viewport height
1978      * @return {Number} The height
1979      */
1980     getViewportHeight: function() {
1981         var height = self.innerHeight;
1982         var mode = document.compatMode;
1983
1984         if ((mode || Roo.isIE) && !Roo.isOpera) {
1985             height = (mode == "CSS1Compat") ?
1986                      document.documentElement.clientHeight :
1987                      document.body.clientHeight;
1988         }
1989
1990         return height;
1991     },
1992     /**
1993      * Get the Window Viewport width
1994      * @return {Number} The width
1995      */
1996     getViewportWidth: function() {
1997         var width = self.innerWidth;
1998         var mode = document.compatMode;
1999
2000         if (mode || Roo.isIE) {
2001             width = (mode == "CSS1Compat") ?
2002                     document.documentElement.clientWidth :
2003                     document.body.clientWidth;
2004         }
2005         return width;
2006     },
2007
2008     isAncestor : function(p, c) {
2009         p = Roo.getDom(p);
2010         c = Roo.getDom(c);
2011         if (!p || !c) {
2012             return false;
2013         }
2014
2015         if (p.contains && !Roo.isSafari) {
2016             return p.contains(c);
2017         } else if (p.compareDocumentPosition) {
2018             return !!(p.compareDocumentPosition(c) & 16);
2019         } else {
2020             var parent = c.parentNode;
2021             while (parent) {
2022                 if (parent == p) {
2023                     return true;
2024                 }
2025                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2026                     return false;
2027                 }
2028                 parent = parent.parentNode;
2029             }
2030             return false;
2031         }
2032     },
2033
2034     getRegion : function(el) {
2035         return Roo.lib.Region.getRegion(el);
2036     },
2037
2038     getY : function(el) {
2039         return this.getXY(el)[1];
2040     },
2041
2042     getX : function(el) {
2043         return this.getXY(el)[0];
2044     },
2045
2046     getXY : function(el) {
2047         var p, pe, b, scroll, bd = document.body;
2048         el = Roo.getDom(el);
2049         var fly = Roo.lib.AnimBase.fly;
2050         if (el.getBoundingClientRect) {
2051             b = el.getBoundingClientRect();
2052             scroll = fly(document).getScroll();
2053             return [b.left + scroll.left, b.top + scroll.top];
2054         }
2055         var x = 0, y = 0;
2056
2057         p = el;
2058
2059         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2060
2061         while (p) {
2062
2063             x += p.offsetLeft;
2064             y += p.offsetTop;
2065
2066             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2067                 hasAbsolute = true;
2068             }
2069
2070             if (Roo.isGecko) {
2071                 pe = fly(p);
2072
2073                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2074                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2075
2076
2077                 x += bl;
2078                 y += bt;
2079
2080
2081                 if (p != el && pe.getStyle('overflow') != 'visible') {
2082                     x += bl;
2083                     y += bt;
2084                 }
2085             }
2086             p = p.offsetParent;
2087         }
2088
2089         if (Roo.isSafari && hasAbsolute) {
2090             x -= bd.offsetLeft;
2091             y -= bd.offsetTop;
2092         }
2093
2094         if (Roo.isGecko && !hasAbsolute) {
2095             var dbd = fly(bd);
2096             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2097             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2098         }
2099
2100         p = el.parentNode;
2101         while (p && p != bd) {
2102             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2103                 x -= p.scrollLeft;
2104                 y -= p.scrollTop;
2105             }
2106             p = p.parentNode;
2107         }
2108         return [x, y];
2109     },
2110  
2111   
2112
2113
2114     setXY : function(el, xy) {
2115         el = Roo.fly(el, '_setXY');
2116         el.position();
2117         var pts = el.translatePoints(xy);
2118         if (xy[0] !== false) {
2119             el.dom.style.left = pts.left + "px";
2120         }
2121         if (xy[1] !== false) {
2122             el.dom.style.top = pts.top + "px";
2123         }
2124     },
2125
2126     setX : function(el, x) {
2127         this.setXY(el, [x, false]);
2128     },
2129
2130     setY : function(el, y) {
2131         this.setXY(el, [false, y]);
2132     }
2133 };
2134 /*
2135  * Portions of this file are based on pieces of Yahoo User Interface Library
2136  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2137  * YUI licensed under the BSD License:
2138  * http://developer.yahoo.net/yui/license.txt
2139  * <script type="text/javascript">
2140  *
2141  */
2142
2143 Roo.lib.Event = function() {
2144     var loadComplete = false;
2145     var listeners = [];
2146     var unloadListeners = [];
2147     var retryCount = 0;
2148     var onAvailStack = [];
2149     var counter = 0;
2150     var lastError = null;
2151
2152     return {
2153         POLL_RETRYS: 200,
2154         POLL_INTERVAL: 20,
2155         EL: 0,
2156         TYPE: 1,
2157         FN: 2,
2158         WFN: 3,
2159         OBJ: 3,
2160         ADJ_SCOPE: 4,
2161         _interval: null,
2162
2163         startInterval: function() {
2164             if (!this._interval) {
2165                 var self = this;
2166                 var callback = function() {
2167                     self._tryPreloadAttach();
2168                 };
2169                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2170
2171             }
2172         },
2173
2174         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2175             onAvailStack.push({ id:         p_id,
2176                 fn:         p_fn,
2177                 obj:        p_obj,
2178                 override:   p_override,
2179                 checkReady: false    });
2180
2181             retryCount = this.POLL_RETRYS;
2182             this.startInterval();
2183         },
2184
2185
2186         addListener: function(el, eventName, fn) {
2187             el = Roo.getDom(el);
2188             if (!el || !fn) {
2189                 return false;
2190             }
2191
2192             if ("unload" == eventName) {
2193                 unloadListeners[unloadListeners.length] =
2194                 [el, eventName, fn];
2195                 return true;
2196             }
2197
2198             var wrappedFn = function(e) {
2199                 return fn(Roo.lib.Event.getEvent(e));
2200             };
2201
2202             var li = [el, eventName, fn, wrappedFn];
2203
2204             var index = listeners.length;
2205             listeners[index] = li;
2206
2207             this.doAdd(el, eventName, wrappedFn, false);
2208             return true;
2209
2210         },
2211
2212
2213         removeListener: function(el, eventName, fn) {
2214             var i, len;
2215
2216             el = Roo.getDom(el);
2217
2218             if(!fn) {
2219                 return this.purgeElement(el, false, eventName);
2220             }
2221
2222
2223             if ("unload" == eventName) {
2224
2225                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2226                     var li = unloadListeners[i];
2227                     if (li &&
2228                         li[0] == el &&
2229                         li[1] == eventName &&
2230                         li[2] == fn) {
2231                         unloadListeners.splice(i, 1);
2232                         return true;
2233                     }
2234                 }
2235
2236                 return false;
2237             }
2238
2239             var cacheItem = null;
2240
2241
2242             var index = arguments[3];
2243
2244             if ("undefined" == typeof index) {
2245                 index = this._getCacheIndex(el, eventName, fn);
2246             }
2247
2248             if (index >= 0) {
2249                 cacheItem = listeners[index];
2250             }
2251
2252             if (!el || !cacheItem) {
2253                 return false;
2254             }
2255
2256             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2257
2258             delete listeners[index][this.WFN];
2259             delete listeners[index][this.FN];
2260             listeners.splice(index, 1);
2261
2262             return true;
2263
2264         },
2265
2266
2267         getTarget: function(ev, resolveTextNode) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var t = ev.target || ev.srcElement;
2271             return this.resolveTextNode(t);
2272         },
2273
2274
2275         resolveTextNode: function(node) {
2276             if (Roo.isSafari && node && 3 == node.nodeType) {
2277                 return node.parentNode;
2278             } else {
2279                 return node;
2280             }
2281         },
2282
2283
2284         getPageX: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             var x = ev.pageX;
2288             if (!x && 0 !== x) {
2289                 x = ev.clientX || 0;
2290
2291                 if (Roo.isIE) {
2292                     x += this.getScroll()[1];
2293                 }
2294             }
2295
2296             return x;
2297         },
2298
2299
2300         getPageY: function(ev) {
2301             ev = ev.browserEvent || ev;
2302             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2303             var y = ev.pageY;
2304             if (!y && 0 !== y) {
2305                 y = ev.clientY || 0;
2306
2307                 if (Roo.isIE) {
2308                     y += this.getScroll()[0];
2309                 }
2310             }
2311
2312
2313             return y;
2314         },
2315
2316
2317         getXY: function(ev) {
2318             ev = ev.browserEvent || ev;
2319             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2320             return [this.getPageX(ev), this.getPageY(ev)];
2321         },
2322
2323
2324         getRelatedTarget: function(ev) {
2325             ev = ev.browserEvent || ev;
2326             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2327             var t = ev.relatedTarget;
2328             if (!t) {
2329                 if (ev.type == "mouseout") {
2330                     t = ev.toElement;
2331                 } else if (ev.type == "mouseover") {
2332                     t = ev.fromElement;
2333                 }
2334             }
2335
2336             return this.resolveTextNode(t);
2337         },
2338
2339
2340         getTime: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2343             if (!ev.time) {
2344                 var t = new Date().getTime();
2345                 try {
2346                     ev.time = t;
2347                 } catch(ex) {
2348                     this.lastError = ex;
2349                     return t;
2350                 }
2351             }
2352
2353             return ev.time;
2354         },
2355
2356
2357         stopEvent: function(ev) {
2358             this.stopPropagation(ev);
2359             this.preventDefault(ev);
2360         },
2361
2362
2363         stopPropagation: function(ev) {
2364             ev = ev.browserEvent || ev;
2365             if (ev.stopPropagation) {
2366                 ev.stopPropagation();
2367             } else {
2368                 ev.cancelBubble = true;
2369             }
2370         },
2371
2372
2373         preventDefault: function(ev) {
2374             ev = ev.browserEvent || ev;
2375             if(ev.preventDefault) {
2376                 ev.preventDefault();
2377             } else {
2378                 ev.returnValue = false;
2379             }
2380         },
2381
2382
2383         getEvent: function(e) {
2384             var ev = e || window.event;
2385             if (!ev) {
2386                 var c = this.getEvent.caller;
2387                 while (c) {
2388                     ev = c.arguments[0];
2389                     if (ev && Event == ev.constructor) {
2390                         break;
2391                     }
2392                     c = c.caller;
2393                 }
2394             }
2395             return ev;
2396         },
2397
2398
2399         getCharCode: function(ev) {
2400             ev = ev.browserEvent || ev;
2401             return ev.charCode || ev.keyCode || 0;
2402         },
2403
2404
2405         _getCacheIndex: function(el, eventName, fn) {
2406             for (var i = 0,len = listeners.length; i < len; ++i) {
2407                 var li = listeners[i];
2408                 if (li &&
2409                     li[this.FN] == fn &&
2410                     li[this.EL] == el &&
2411                     li[this.TYPE] == eventName) {
2412                     return i;
2413                 }
2414             }
2415
2416             return -1;
2417         },
2418
2419
2420         elCache: {},
2421
2422
2423         getEl: function(id) {
2424             return document.getElementById(id);
2425         },
2426
2427
2428         clearCache: function() {
2429         },
2430
2431
2432         _load: function(e) {
2433             loadComplete = true;
2434             var EU = Roo.lib.Event;
2435
2436
2437             if (Roo.isIE) {
2438                 EU.doRemove(window, "load", EU._load);
2439             }
2440         },
2441
2442
2443         _tryPreloadAttach: function() {
2444
2445             if (this.locked) {
2446                 return false;
2447             }
2448
2449             this.locked = true;
2450
2451
2452             var tryAgain = !loadComplete;
2453             if (!tryAgain) {
2454                 tryAgain = (retryCount > 0);
2455             }
2456
2457
2458             var notAvail = [];
2459             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2460                 var item = onAvailStack[i];
2461                 if (item) {
2462                     var el = this.getEl(item.id);
2463
2464                     if (el) {
2465                         if (!item.checkReady ||
2466                             loadComplete ||
2467                             el.nextSibling ||
2468                             (document && document.body)) {
2469
2470                             var scope = el;
2471                             if (item.override) {
2472                                 if (item.override === true) {
2473                                     scope = item.obj;
2474                                 } else {
2475                                     scope = item.override;
2476                                 }
2477                             }
2478                             item.fn.call(scope, item.obj);
2479                             onAvailStack[i] = null;
2480                         }
2481                     } else {
2482                         notAvail.push(item);
2483                     }
2484                 }
2485             }
2486
2487             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2488
2489             if (tryAgain) {
2490
2491                 this.startInterval();
2492             } else {
2493                 clearInterval(this._interval);
2494                 this._interval = null;
2495             }
2496
2497             this.locked = false;
2498
2499             return true;
2500
2501         },
2502
2503
2504         purgeElement: function(el, recurse, eventName) {
2505             var elListeners = this.getListeners(el, eventName);
2506             if (elListeners) {
2507                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2508                     var l = elListeners[i];
2509                     this.removeListener(el, l.type, l.fn);
2510                 }
2511             }
2512
2513             if (recurse && el && el.childNodes) {
2514                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2515                     this.purgeElement(el.childNodes[i], recurse, eventName);
2516                 }
2517             }
2518         },
2519
2520
2521         getListeners: function(el, eventName) {
2522             var results = [], searchLists;
2523             if (!eventName) {
2524                 searchLists = [listeners, unloadListeners];
2525             } else if (eventName == "unload") {
2526                 searchLists = [unloadListeners];
2527             } else {
2528                 searchLists = [listeners];
2529             }
2530
2531             for (var j = 0; j < searchLists.length; ++j) {
2532                 var searchList = searchLists[j];
2533                 if (searchList && searchList.length > 0) {
2534                     for (var i = 0,len = searchList.length; i < len; ++i) {
2535                         var l = searchList[i];
2536                         if (l && l[this.EL] === el &&
2537                             (!eventName || eventName === l[this.TYPE])) {
2538                             results.push({
2539                                 type:   l[this.TYPE],
2540                                 fn:     l[this.FN],
2541                                 obj:    l[this.OBJ],
2542                                 adjust: l[this.ADJ_SCOPE],
2543                                 index:  i
2544                             });
2545                         }
2546                     }
2547                 }
2548             }
2549
2550             return (results.length) ? results : null;
2551         },
2552
2553
2554         _unload: function(e) {
2555
2556             var EU = Roo.lib.Event, i, j, l, len, index;
2557
2558             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2559                 l = unloadListeners[i];
2560                 if (l) {
2561                     var scope = window;
2562                     if (l[EU.ADJ_SCOPE]) {
2563                         if (l[EU.ADJ_SCOPE] === true) {
2564                             scope = l[EU.OBJ];
2565                         } else {
2566                             scope = l[EU.ADJ_SCOPE];
2567                         }
2568                     }
2569                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2570                     unloadListeners[i] = null;
2571                     l = null;
2572                     scope = null;
2573                 }
2574             }
2575
2576             unloadListeners = null;
2577
2578             if (listeners && listeners.length > 0) {
2579                 j = listeners.length;
2580                 while (j) {
2581                     index = j - 1;
2582                     l = listeners[index];
2583                     if (l) {
2584                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2585                                 l[EU.FN], index);
2586                     }
2587                     j = j - 1;
2588                 }
2589                 l = null;
2590
2591                 EU.clearCache();
2592             }
2593
2594             EU.doRemove(window, "unload", EU._unload);
2595
2596         },
2597
2598
2599         getScroll: function() {
2600             var dd = document.documentElement, db = document.body;
2601             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2602                 return [dd.scrollTop, dd.scrollLeft];
2603             } else if (db) {
2604                 return [db.scrollTop, db.scrollLeft];
2605             } else {
2606                 return [0, 0];
2607             }
2608         },
2609
2610
2611         doAdd: function () {
2612             if (window.addEventListener) {
2613                 return function(el, eventName, fn, capture) {
2614                     el.addEventListener(eventName, fn, (capture));
2615                 };
2616             } else if (window.attachEvent) {
2617                 return function(el, eventName, fn, capture) {
2618                     el.attachEvent("on" + eventName, fn);
2619                 };
2620             } else {
2621                 return function() {
2622                 };
2623             }
2624         }(),
2625
2626
2627         doRemove: function() {
2628             if (window.removeEventListener) {
2629                 return function (el, eventName, fn, capture) {
2630                     el.removeEventListener(eventName, fn, (capture));
2631                 };
2632             } else if (window.detachEvent) {
2633                 return function (el, eventName, fn) {
2634                     el.detachEvent("on" + eventName, fn);
2635                 };
2636             } else {
2637                 return function() {
2638                 };
2639             }
2640         }()
2641     };
2642     
2643 }();
2644 (function() {     
2645    
2646     var E = Roo.lib.Event;
2647     E.on = E.addListener;
2648     E.un = E.removeListener;
2649
2650     if (document && document.body) {
2651         E._load();
2652     } else {
2653         E.doAdd(window, "load", E._load);
2654     }
2655     E.doAdd(window, "unload", E._unload);
2656     E._tryPreloadAttach();
2657 })();
2658
2659  
2660
2661 (function() {
2662     /**
2663      * @class Roo.lib.Ajax
2664      *
2665      * provide a simple Ajax request utility functions
2666      * 
2667      * Portions of this file are based on pieces of Yahoo User Interface Library
2668     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2669     * YUI licensed under the BSD License:
2670     * http://developer.yahoo.net/yui/license.txt
2671     * <script type="text/javascript">
2672     *
2673      *
2674      */
2675     Roo.lib.Ajax = {
2676         /**
2677          * @static 
2678          */
2679         request : function(method, uri, cb, data, options) {
2680             if(options){
2681                 var hs = options.headers;
2682                 if(hs){
2683                     for(var h in hs){
2684                         if(hs.hasOwnProperty(h)){
2685                             this.initHeader(h, hs[h], false);
2686                         }
2687                     }
2688                 }
2689                 if(options.xmlData){
2690                     this.initHeader('Content-Type', 'text/xml', false);
2691                     method = 'POST';
2692                     data = options.xmlData;
2693                 }
2694             }
2695
2696             return this.asyncRequest(method, uri, cb, data);
2697         },
2698         /**
2699          * serialize a form
2700          *
2701          * @static
2702          * @param {DomForm} form element
2703          * @return {String} urlencode form output.
2704          */
2705         serializeForm : function(form) {
2706             if(typeof form == 'string') {
2707                 form = (document.getElementById(form) || document.forms[form]);
2708             }
2709
2710             var el, name, val, disabled, data = '', hasSubmit = false;
2711             for (var i = 0; i < form.elements.length; i++) {
2712                 el = form.elements[i];
2713                 disabled = form.elements[i].disabled;
2714                 name = form.elements[i].name;
2715                 val = form.elements[i].value;
2716
2717                 if (!disabled && name){
2718                     switch (el.type)
2719                             {
2720                         case 'select-one':
2721                         case 'select-multiple':
2722                             for (var j = 0; j < el.options.length; j++) {
2723                                 if (el.options[j].selected) {
2724                                     if (Roo.isIE) {
2725                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2726                                     }
2727                                     else {
2728                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2729                                     }
2730                                 }
2731                             }
2732                             break;
2733                         case 'radio':
2734                         case 'checkbox':
2735                             if (el.checked) {
2736                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2737                             }
2738                             break;
2739                         case 'file':
2740
2741                         case undefined:
2742
2743                         case 'reset':
2744
2745                         case 'button':
2746
2747                             break;
2748                         case 'submit':
2749                             if(hasSubmit == false) {
2750                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2751                                 hasSubmit = true;
2752                             }
2753                             break;
2754                         default:
2755                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2756                             break;
2757                     }
2758                 }
2759             }
2760             data = data.substr(0, data.length - 1);
2761             return data;
2762         },
2763
2764         headers:{},
2765
2766         hasHeaders:false,
2767
2768         useDefaultHeader:true,
2769
2770         defaultPostHeader:'application/x-www-form-urlencoded',
2771
2772         useDefaultXhrHeader:true,
2773
2774         defaultXhrHeader:'XMLHttpRequest',
2775
2776         hasDefaultHeaders:true,
2777
2778         defaultHeaders:{},
2779
2780         poll:{},
2781
2782         timeout:{},
2783
2784         pollInterval:50,
2785
2786         transactionId:0,
2787
2788         setProgId:function(id)
2789         {
2790             this.activeX.unshift(id);
2791         },
2792
2793         setDefaultPostHeader:function(b)
2794         {
2795             this.useDefaultHeader = b;
2796         },
2797
2798         setDefaultXhrHeader:function(b)
2799         {
2800             this.useDefaultXhrHeader = b;
2801         },
2802
2803         setPollingInterval:function(i)
2804         {
2805             if (typeof i == 'number' && isFinite(i)) {
2806                 this.pollInterval = i;
2807             }
2808         },
2809
2810         createXhrObject:function(transactionId)
2811         {
2812             var obj,http;
2813             try
2814             {
2815
2816                 http = new XMLHttpRequest();
2817
2818                 obj = { conn:http, tId:transactionId };
2819             }
2820             catch(e)
2821             {
2822                 for (var i = 0; i < this.activeX.length; ++i) {
2823                     try
2824                     {
2825
2826                         http = new ActiveXObject(this.activeX[i]);
2827
2828                         obj = { conn:http, tId:transactionId };
2829                         break;
2830                     }
2831                     catch(e) {
2832                     }
2833                 }
2834             }
2835             finally
2836             {
2837                 return obj;
2838             }
2839         },
2840
2841         getConnectionObject:function()
2842         {
2843             var o;
2844             var tId = this.transactionId;
2845
2846             try
2847             {
2848                 o = this.createXhrObject(tId);
2849                 if (o) {
2850                     this.transactionId++;
2851                 }
2852             }
2853             catch(e) {
2854             }
2855             finally
2856             {
2857                 return o;
2858             }
2859         },
2860
2861         asyncRequest:function(method, uri, callback, postData)
2862         {
2863             var o = this.getConnectionObject();
2864
2865             if (!o) {
2866                 return null;
2867             }
2868             else {
2869                 o.conn.open(method, uri, true);
2870
2871                 if (this.useDefaultXhrHeader) {
2872                     if (!this.defaultHeaders['X-Requested-With']) {
2873                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2874                     }
2875                 }
2876
2877                 if(postData && this.useDefaultHeader){
2878                     this.initHeader('Content-Type', this.defaultPostHeader);
2879                 }
2880
2881                  if (this.hasDefaultHeaders || this.hasHeaders) {
2882                     this.setHeader(o);
2883                 }
2884
2885                 this.handleReadyState(o, callback);
2886                 o.conn.send(postData || null);
2887
2888                 return o;
2889             }
2890         },
2891
2892         handleReadyState:function(o, callback)
2893         {
2894             var oConn = this;
2895
2896             if (callback && callback.timeout) {
2897                 
2898                 this.timeout[o.tId] = window.setTimeout(function() {
2899                     oConn.abort(o, callback, true);
2900                 }, callback.timeout);
2901             }
2902
2903             this.poll[o.tId] = window.setInterval(
2904                     function() {
2905                         if (o.conn && o.conn.readyState == 4) {
2906                             window.clearInterval(oConn.poll[o.tId]);
2907                             delete oConn.poll[o.tId];
2908
2909                             if(callback && callback.timeout) {
2910                                 window.clearTimeout(oConn.timeout[o.tId]);
2911                                 delete oConn.timeout[o.tId];
2912                             }
2913
2914                             oConn.handleTransactionResponse(o, callback);
2915                         }
2916                     }
2917                     , this.pollInterval);
2918         },
2919
2920         handleTransactionResponse:function(o, callback, isAbort)
2921         {
2922
2923             if (!callback) {
2924                 this.releaseObject(o);
2925                 return;
2926             }
2927
2928             var httpStatus, responseObject;
2929
2930             try
2931             {
2932                 if (o.conn.status !== undefined && o.conn.status != 0) {
2933                     httpStatus = o.conn.status;
2934                 }
2935                 else {
2936                     httpStatus = 13030;
2937                 }
2938             }
2939             catch(e) {
2940
2941
2942                 httpStatus = 13030;
2943             }
2944
2945             if (httpStatus >= 200 && httpStatus < 300) {
2946                 responseObject = this.createResponseObject(o, callback.argument);
2947                 if (callback.success) {
2948                     if (!callback.scope) {
2949                         callback.success(responseObject);
2950                     }
2951                     else {
2952
2953
2954                         callback.success.apply(callback.scope, [responseObject]);
2955                     }
2956                 }
2957             }
2958             else {
2959                 switch (httpStatus) {
2960
2961                     case 12002:
2962                     case 12029:
2963                     case 12030:
2964                     case 12031:
2965                     case 12152:
2966                     case 13030:
2967                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2968                         if (callback.failure) {
2969                             if (!callback.scope) {
2970                                 callback.failure(responseObject);
2971                             }
2972                             else {
2973                                 callback.failure.apply(callback.scope, [responseObject]);
2974                             }
2975                         }
2976                         break;
2977                     default:
2978                         responseObject = this.createResponseObject(o, callback.argument);
2979                         if (callback.failure) {
2980                             if (!callback.scope) {
2981                                 callback.failure(responseObject);
2982                             }
2983                             else {
2984                                 callback.failure.apply(callback.scope, [responseObject]);
2985                             }
2986                         }
2987                 }
2988             }
2989
2990             this.releaseObject(o);
2991             responseObject = null;
2992         },
2993
2994         createResponseObject:function(o, callbackArg)
2995         {
2996             var obj = {};
2997             var headerObj = {};
2998
2999             try
3000             {
3001                 var headerStr = o.conn.getAllResponseHeaders();
3002                 var header = headerStr.split('\n');
3003                 for (var i = 0; i < header.length; i++) {
3004                     var delimitPos = header[i].indexOf(':');
3005                     if (delimitPos != -1) {
3006                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3007                     }
3008                 }
3009             }
3010             catch(e) {
3011             }
3012
3013             obj.tId = o.tId;
3014             obj.status = o.conn.status;
3015             obj.statusText = o.conn.statusText;
3016             obj.getResponseHeader = headerObj;
3017             obj.getAllResponseHeaders = headerStr;
3018             obj.responseText = o.conn.responseText;
3019             obj.responseXML = o.conn.responseXML;
3020
3021             if (typeof callbackArg !== undefined) {
3022                 obj.argument = callbackArg;
3023             }
3024
3025             return obj;
3026         },
3027
3028         createExceptionObject:function(tId, callbackArg, isAbort)
3029         {
3030             var COMM_CODE = 0;
3031             var COMM_ERROR = 'communication failure';
3032             var ABORT_CODE = -1;
3033             var ABORT_ERROR = 'transaction aborted';
3034
3035             var obj = {};
3036
3037             obj.tId = tId;
3038             if (isAbort) {
3039                 obj.status = ABORT_CODE;
3040                 obj.statusText = ABORT_ERROR;
3041             }
3042             else {
3043                 obj.status = COMM_CODE;
3044                 obj.statusText = COMM_ERROR;
3045             }
3046
3047             if (callbackArg) {
3048                 obj.argument = callbackArg;
3049             }
3050
3051             return obj;
3052         },
3053
3054         initHeader:function(label, value, isDefault)
3055         {
3056             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3057
3058             if (headerObj[label] === undefined) {
3059                 headerObj[label] = value;
3060             }
3061             else {
3062
3063
3064                 headerObj[label] = value + "," + headerObj[label];
3065             }
3066
3067             if (isDefault) {
3068                 this.hasDefaultHeaders = true;
3069             }
3070             else {
3071                 this.hasHeaders = true;
3072             }
3073         },
3074
3075
3076         setHeader:function(o)
3077         {
3078             if (this.hasDefaultHeaders) {
3079                 for (var prop in this.defaultHeaders) {
3080                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3081                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3082                     }
3083                 }
3084             }
3085
3086             if (this.hasHeaders) {
3087                 for (var prop in this.headers) {
3088                     if (this.headers.hasOwnProperty(prop)) {
3089                         o.conn.setRequestHeader(prop, this.headers[prop]);
3090                     }
3091                 }
3092                 this.headers = {};
3093                 this.hasHeaders = false;
3094             }
3095         },
3096
3097         resetDefaultHeaders:function() {
3098             delete this.defaultHeaders;
3099             this.defaultHeaders = {};
3100             this.hasDefaultHeaders = false;
3101         },
3102
3103         abort:function(o, callback, isTimeout)
3104         {
3105             if(this.isCallInProgress(o)) {
3106                 o.conn.abort();
3107                 window.clearInterval(this.poll[o.tId]);
3108                 delete this.poll[o.tId];
3109                 if (isTimeout) {
3110                     delete this.timeout[o.tId];
3111                 }
3112
3113                 this.handleTransactionResponse(o, callback, true);
3114
3115                 return true;
3116             }
3117             else {
3118                 return false;
3119             }
3120         },
3121
3122
3123         isCallInProgress:function(o)
3124         {
3125             if (o && o.conn) {
3126                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3127             }
3128             else {
3129
3130                 return false;
3131             }
3132         },
3133
3134
3135         releaseObject:function(o)
3136         {
3137
3138             o.conn = null;
3139
3140             o = null;
3141         },
3142
3143         activeX:[
3144         'MSXML2.XMLHTTP.3.0',
3145         'MSXML2.XMLHTTP',
3146         'Microsoft.XMLHTTP'
3147         ]
3148
3149
3150     };
3151 })();/*
3152  * Portions of this file are based on pieces of Yahoo User Interface Library
3153  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3154  * YUI licensed under the BSD License:
3155  * http://developer.yahoo.net/yui/license.txt
3156  * <script type="text/javascript">
3157  *
3158  */
3159
3160 Roo.lib.Region = function(t, r, b, l) {
3161     this.top = t;
3162     this[1] = t;
3163     this.right = r;
3164     this.bottom = b;
3165     this.left = l;
3166     this[0] = l;
3167 };
3168
3169
3170 Roo.lib.Region.prototype = {
3171     contains : function(region) {
3172         return ( region.left >= this.left &&
3173                  region.right <= this.right &&
3174                  region.top >= this.top &&
3175                  region.bottom <= this.bottom    );
3176
3177     },
3178
3179     getArea : function() {
3180         return ( (this.bottom - this.top) * (this.right - this.left) );
3181     },
3182
3183     intersect : function(region) {
3184         var t = Math.max(this.top, region.top);
3185         var r = Math.min(this.right, region.right);
3186         var b = Math.min(this.bottom, region.bottom);
3187         var l = Math.max(this.left, region.left);
3188
3189         if (b >= t && r >= l) {
3190             return new Roo.lib.Region(t, r, b, l);
3191         } else {
3192             return null;
3193         }
3194     },
3195     union : function(region) {
3196         var t = Math.min(this.top, region.top);
3197         var r = Math.max(this.right, region.right);
3198         var b = Math.max(this.bottom, region.bottom);
3199         var l = Math.min(this.left, region.left);
3200
3201         return new Roo.lib.Region(t, r, b, l);
3202     },
3203
3204     adjust : function(t, l, b, r) {
3205         this.top += t;
3206         this.left += l;
3207         this.right += r;
3208         this.bottom += b;
3209         return this;
3210     }
3211 };
3212
3213 Roo.lib.Region.getRegion = function(el) {
3214     var p = Roo.lib.Dom.getXY(el);
3215
3216     var t = p[1];
3217     var r = p[0] + el.offsetWidth;
3218     var b = p[1] + el.offsetHeight;
3219     var l = p[0];
3220
3221     return new Roo.lib.Region(t, r, b, l);
3222 };
3223 /*
3224  * Portions of this file are based on pieces of Yahoo User Interface Library
3225  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3226  * YUI licensed under the BSD License:
3227  * http://developer.yahoo.net/yui/license.txt
3228  * <script type="text/javascript">
3229  *
3230  */
3231 //@@dep Roo.lib.Region
3232
3233
3234 Roo.lib.Point = function(x, y) {
3235     if (x instanceof Array) {
3236         y = x[1];
3237         x = x[0];
3238     }
3239     this.x = this.right = this.left = this[0] = x;
3240     this.y = this.top = this.bottom = this[1] = y;
3241 };
3242
3243 Roo.lib.Point.prototype = new Roo.lib.Region();
3244 /*
3245  * Portions of this file are based on pieces of Yahoo User Interface Library
3246  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3247  * YUI licensed under the BSD License:
3248  * http://developer.yahoo.net/yui/license.txt
3249  * <script type="text/javascript">
3250  *
3251  */
3252  
3253 (function() {   
3254
3255     Roo.lib.Anim = {
3256         scroll : function(el, args, duration, easing, cb, scope) {
3257             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3258         },
3259
3260         motion : function(el, args, duration, easing, cb, scope) {
3261             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3262         },
3263
3264         color : function(el, args, duration, easing, cb, scope) {
3265             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3266         },
3267
3268         run : function(el, args, duration, easing, cb, scope, type) {
3269             type = type || Roo.lib.AnimBase;
3270             if (typeof easing == "string") {
3271                 easing = Roo.lib.Easing[easing];
3272             }
3273             var anim = new type(el, args, duration, easing);
3274             anim.animateX(function() {
3275                 Roo.callback(cb, scope);
3276             });
3277             return anim;
3278         }
3279     };
3280 })();/*
3281  * Portions of this file are based on pieces of Yahoo User Interface Library
3282  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3283  * YUI licensed under the BSD License:
3284  * http://developer.yahoo.net/yui/license.txt
3285  * <script type="text/javascript">
3286  *
3287  */
3288
3289 (function() {    
3290     var libFlyweight;
3291     
3292     function fly(el) {
3293         if (!libFlyweight) {
3294             libFlyweight = new Roo.Element.Flyweight();
3295         }
3296         libFlyweight.dom = el;
3297         return libFlyweight;
3298     }
3299
3300     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3301     
3302    
3303     
3304     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3305         if (el) {
3306             this.init(el, attributes, duration, method);
3307         }
3308     };
3309
3310     Roo.lib.AnimBase.fly = fly;
3311     
3312     
3313     
3314     Roo.lib.AnimBase.prototype = {
3315
3316         toString: function() {
3317             var el = this.getEl();
3318             var id = el.id || el.tagName;
3319             return ("Anim " + id);
3320         },
3321
3322         patterns: {
3323             noNegatives:        /width|height|opacity|padding/i,
3324             offsetAttribute:  /^((width|height)|(top|left))$/,
3325             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3326             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3327         },
3328
3329
3330         doMethod: function(attr, start, end) {
3331             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3332         },
3333
3334
3335         setAttribute: function(attr, val, unit) {
3336             if (this.patterns.noNegatives.test(attr)) {
3337                 val = (val > 0) ? val : 0;
3338             }
3339
3340             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3341         },
3342
3343
3344         getAttribute: function(attr) {
3345             var el = this.getEl();
3346             var val = fly(el).getStyle(attr);
3347
3348             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3349                 return parseFloat(val);
3350             }
3351
3352             var a = this.patterns.offsetAttribute.exec(attr) || [];
3353             var pos = !!( a[3] );
3354             var box = !!( a[2] );
3355
3356
3357             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3358                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3359             } else {
3360                 val = 0;
3361             }
3362
3363             return val;
3364         },
3365
3366
3367         getDefaultUnit: function(attr) {
3368             if (this.patterns.defaultUnit.test(attr)) {
3369                 return 'px';
3370             }
3371
3372             return '';
3373         },
3374
3375         animateX : function(callback, scope) {
3376             var f = function() {
3377                 this.onComplete.removeListener(f);
3378                 if (typeof callback == "function") {
3379                     callback.call(scope || this, this);
3380                 }
3381             };
3382             this.onComplete.addListener(f, this);
3383             this.animate();
3384         },
3385
3386
3387         setRuntimeAttribute: function(attr) {
3388             var start;
3389             var end;
3390             var attributes = this.attributes;
3391
3392             this.runtimeAttributes[attr] = {};
3393
3394             var isset = function(prop) {
3395                 return (typeof prop !== 'undefined');
3396             };
3397
3398             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3399                 return false;
3400             }
3401
3402             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3403
3404
3405             if (isset(attributes[attr]['to'])) {
3406                 end = attributes[attr]['to'];
3407             } else if (isset(attributes[attr]['by'])) {
3408                 if (start.constructor == Array) {
3409                     end = [];
3410                     for (var i = 0, len = start.length; i < len; ++i) {
3411                         end[i] = start[i] + attributes[attr]['by'][i];
3412                     }
3413                 } else {
3414                     end = start + attributes[attr]['by'];
3415                 }
3416             }
3417
3418             this.runtimeAttributes[attr].start = start;
3419             this.runtimeAttributes[attr].end = end;
3420
3421
3422             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3423         },
3424
3425
3426         init: function(el, attributes, duration, method) {
3427
3428             var isAnimated = false;
3429
3430
3431             var startTime = null;
3432
3433
3434             var actualFrames = 0;
3435
3436
3437             el = Roo.getDom(el);
3438
3439
3440             this.attributes = attributes || {};
3441
3442
3443             this.duration = duration || 1;
3444
3445
3446             this.method = method || Roo.lib.Easing.easeNone;
3447
3448
3449             this.useSeconds = true;
3450
3451
3452             this.currentFrame = 0;
3453
3454
3455             this.totalFrames = Roo.lib.AnimMgr.fps;
3456
3457
3458             this.getEl = function() {
3459                 return el;
3460             };
3461
3462
3463             this.isAnimated = function() {
3464                 return isAnimated;
3465             };
3466
3467
3468             this.getStartTime = function() {
3469                 return startTime;
3470             };
3471
3472             this.runtimeAttributes = {};
3473
3474
3475             this.animate = function() {
3476                 if (this.isAnimated()) {
3477                     return false;
3478                 }
3479
3480                 this.currentFrame = 0;
3481
3482                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3483
3484                 Roo.lib.AnimMgr.registerElement(this);
3485             };
3486
3487
3488             this.stop = function(finish) {
3489                 if (finish) {
3490                     this.currentFrame = this.totalFrames;
3491                     this._onTween.fire();
3492                 }
3493                 Roo.lib.AnimMgr.stop(this);
3494             };
3495
3496             var onStart = function() {
3497                 this.onStart.fire();
3498
3499                 this.runtimeAttributes = {};
3500                 for (var attr in this.attributes) {
3501                     this.setRuntimeAttribute(attr);
3502                 }
3503
3504                 isAnimated = true;
3505                 actualFrames = 0;
3506                 startTime = new Date();
3507             };
3508
3509
3510             var onTween = function() {
3511                 var data = {
3512                     duration: new Date() - this.getStartTime(),
3513                     currentFrame: this.currentFrame
3514                 };
3515
3516                 data.toString = function() {
3517                     return (
3518                             'duration: ' + data.duration +
3519                             ', currentFrame: ' + data.currentFrame
3520                             );
3521                 };
3522
3523                 this.onTween.fire(data);
3524
3525                 var runtimeAttributes = this.runtimeAttributes;
3526
3527                 for (var attr in runtimeAttributes) {
3528                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3529                 }
3530
3531                 actualFrames += 1;
3532             };
3533
3534             var onComplete = function() {
3535                 var actual_duration = (new Date() - startTime) / 1000 ;
3536
3537                 var data = {
3538                     duration: actual_duration,
3539                     frames: actualFrames,
3540                     fps: actualFrames / actual_duration
3541                 };
3542
3543                 data.toString = function() {
3544                     return (
3545                             'duration: ' + data.duration +
3546                             ', frames: ' + data.frames +
3547                             ', fps: ' + data.fps
3548                             );
3549                 };
3550
3551                 isAnimated = false;
3552                 actualFrames = 0;
3553                 this.onComplete.fire(data);
3554             };
3555
3556
3557             this._onStart = new Roo.util.Event(this);
3558             this.onStart = new Roo.util.Event(this);
3559             this.onTween = new Roo.util.Event(this);
3560             this._onTween = new Roo.util.Event(this);
3561             this.onComplete = new Roo.util.Event(this);
3562             this._onComplete = new Roo.util.Event(this);
3563             this._onStart.addListener(onStart);
3564             this._onTween.addListener(onTween);
3565             this._onComplete.addListener(onComplete);
3566         }
3567     };
3568 })();
3569 /*
3570  * Portions of this file are based on pieces of Yahoo User Interface Library
3571  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3572  * YUI licensed under the BSD License:
3573  * http://developer.yahoo.net/yui/license.txt
3574  * <script type="text/javascript">
3575  *
3576  */
3577
3578 Roo.lib.AnimMgr = new function() {
3579
3580     var thread = null;
3581
3582
3583     var queue = [];
3584
3585
3586     var tweenCount = 0;
3587
3588
3589     this.fps = 1000;
3590
3591
3592     this.delay = 1;
3593
3594
3595     this.registerElement = function(tween) {
3596         queue[queue.length] = tween;
3597         tweenCount += 1;
3598         tween._onStart.fire();
3599         this.start();
3600     };
3601
3602
3603     this.unRegister = function(tween, index) {
3604         tween._onComplete.fire();
3605         index = index || getIndex(tween);
3606         if (index != -1) {
3607             queue.splice(index, 1);
3608         }
3609
3610         tweenCount -= 1;
3611         if (tweenCount <= 0) {
3612             this.stop();
3613         }
3614     };
3615
3616
3617     this.start = function() {
3618         if (thread === null) {
3619             thread = setInterval(this.run, this.delay);
3620         }
3621     };
3622
3623
3624     this.stop = function(tween) {
3625         if (!tween) {
3626             clearInterval(thread);
3627
3628             for (var i = 0, len = queue.length; i < len; ++i) {
3629                 if (queue[0].isAnimated()) {
3630                     this.unRegister(queue[0], 0);
3631                 }
3632             }
3633
3634             queue = [];
3635             thread = null;
3636             tweenCount = 0;
3637         }
3638         else {
3639             this.unRegister(tween);
3640         }
3641     };
3642
3643
3644     this.run = function() {
3645         for (var i = 0, len = queue.length; i < len; ++i) {
3646             var tween = queue[i];
3647             if (!tween || !tween.isAnimated()) {
3648                 continue;
3649             }
3650
3651             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3652             {
3653                 tween.currentFrame += 1;
3654
3655                 if (tween.useSeconds) {
3656                     correctFrame(tween);
3657                 }
3658                 tween._onTween.fire();
3659             }
3660             else {
3661                 Roo.lib.AnimMgr.stop(tween, i);
3662             }
3663         }
3664     };
3665
3666     var getIndex = function(anim) {
3667         for (var i = 0, len = queue.length; i < len; ++i) {
3668             if (queue[i] == anim) {
3669                 return i;
3670             }
3671         }
3672         return -1;
3673     };
3674
3675
3676     var correctFrame = function(tween) {
3677         var frames = tween.totalFrames;
3678         var frame = tween.currentFrame;
3679         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3680         var elapsed = (new Date() - tween.getStartTime());
3681         var tweak = 0;
3682
3683         if (elapsed < tween.duration * 1000) {
3684             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3685         } else {
3686             tweak = frames - (frame + 1);
3687         }
3688         if (tweak > 0 && isFinite(tweak)) {
3689             if (tween.currentFrame + tweak >= frames) {
3690                 tweak = frames - (frame + 1);
3691             }
3692
3693             tween.currentFrame += tweak;
3694         }
3695     };
3696 };
3697
3698     /*
3699  * Portions of this file are based on pieces of Yahoo User Interface Library
3700  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701  * YUI licensed under the BSD License:
3702  * http://developer.yahoo.net/yui/license.txt
3703  * <script type="text/javascript">
3704  *
3705  */
3706 Roo.lib.Bezier = new function() {
3707
3708         this.getPosition = function(points, t) {
3709             var n = points.length;
3710             var tmp = [];
3711
3712             for (var i = 0; i < n; ++i) {
3713                 tmp[i] = [points[i][0], points[i][1]];
3714             }
3715
3716             for (var j = 1; j < n; ++j) {
3717                 for (i = 0; i < n - j; ++i) {
3718                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3719                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3720                 }
3721             }
3722
3723             return [ tmp[0][0], tmp[0][1] ];
3724
3725         };
3726     }; 
3727
3728 /**
3729  * @class Roo.lib.Color
3730  * @constructor
3731  * An abstract Color implementation. Concrete Color implementations should use
3732  * an instance of this function as their prototype, and implement the getRGB and
3733  * getHSL functions. getRGB should return an object representing the RGB
3734  * components of this Color, with the red, green, and blue components in the
3735  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3736  * return an object representing the HSL components of this Color, with the hue
3737  * component in the range [0,360), the saturation and lightness components in
3738  * the range [0,100], and the alpha component in the range [0,1].
3739  *
3740  *
3741  * Color.js
3742  *
3743  * Functions for Color handling and processing.
3744  *
3745  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3746  *
3747  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3748  * rights to this program, with the intention of it becoming part of the public
3749  * domain. Because this program is released into the public domain, it comes with
3750  * no warranty either expressed or implied, to the extent permitted by law.
3751  * 
3752  * For more free and public domain JavaScript code by the same author, visit:
3753  * http://www.safalra.com/web-design/javascript/
3754  * 
3755  */
3756 Roo.lib.Color = function() { }
3757
3758
3759 Roo.apply(Roo.lib.Color.prototype, {
3760   
3761   rgb : null,
3762   hsv : null,
3763   hsl : null,
3764   
3765   /**
3766    * getIntegerRGB
3767    * @return {Object} an object representing the RGBA components of this Color. The red,
3768    * green, and blue components are converted to integers in the range [0,255].
3769    * The alpha is a value in the range [0,1].
3770    */
3771   getIntegerRGB : function(){
3772
3773     // get the RGB components of this Color
3774     var rgb = this.getRGB();
3775
3776     // return the integer components
3777     return {
3778       'r' : Math.round(rgb.r),
3779       'g' : Math.round(rgb.g),
3780       'b' : Math.round(rgb.b),
3781       'a' : rgb.a
3782     };
3783
3784   },
3785
3786   /**
3787    * getPercentageRGB
3788    * @return {Object} an object representing the RGBA components of this Color. The red,
3789    * green, and blue components are converted to numbers in the range [0,100].
3790    * The alpha is a value in the range [0,1].
3791    */
3792   getPercentageRGB : function(){
3793
3794     // get the RGB components of this Color
3795     var rgb = this.getRGB();
3796
3797     // return the percentage components
3798     return {
3799       'r' : 100 * rgb.r / 255,
3800       'g' : 100 * rgb.g / 255,
3801       'b' : 100 * rgb.b / 255,
3802       'a' : rgb.a
3803     };
3804
3805   },
3806
3807   /**
3808    * getCSSHexadecimalRGB
3809    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3810    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3811    * are two-digit hexadecimal numbers.
3812    */
3813   getCSSHexadecimalRGB : function()
3814   {
3815
3816     // get the integer RGB components
3817     var rgb = this.getIntegerRGB();
3818
3819     // determine the hexadecimal equivalents
3820     var r16 = rgb.r.toString(16);
3821     var g16 = rgb.g.toString(16);
3822     var b16 = rgb.b.toString(16);
3823
3824     // return the CSS RGB Color value
3825     return '#'
3826         + (r16.length == 2 ? r16 : '0' + r16)
3827         + (g16.length == 2 ? g16 : '0' + g16)
3828         + (b16.length == 2 ? b16 : '0' + b16);
3829
3830   },
3831
3832   /**
3833    * getCSSIntegerRGB
3834    * @return {String} a string representing this Color as a CSS integer RGB Color
3835    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3836    * are integers in the range [0,255].
3837    */
3838   getCSSIntegerRGB : function(){
3839
3840     // get the integer RGB components
3841     var rgb = this.getIntegerRGB();
3842
3843     // return the CSS RGB Color value
3844     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3845
3846   },
3847
3848   /**
3849    * getCSSIntegerRGBA
3850    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3851    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3852    * b are integers in the range [0,255] and a is in the range [0,1].
3853    */
3854   getCSSIntegerRGBA : function(){
3855
3856     // get the integer RGB components
3857     var rgb = this.getIntegerRGB();
3858
3859     // return the CSS integer RGBA Color value
3860     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3861
3862   },
3863
3864   /**
3865    * getCSSPercentageRGB
3866    * @return {String} a string representing this Color as a CSS percentage RGB Color
3867    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3868    * b are in the range [0,100].
3869    */
3870   getCSSPercentageRGB : function(){
3871
3872     // get the percentage RGB components
3873     var rgb = this.getPercentageRGB();
3874
3875     // return the CSS RGB Color value
3876     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3877
3878   },
3879
3880   /**
3881    * getCSSPercentageRGBA
3882    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3883    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3884    * and b are in the range [0,100] and a is in the range [0,1].
3885    */
3886   getCSSPercentageRGBA : function(){
3887
3888     // get the percentage RGB components
3889     var rgb = this.getPercentageRGB();
3890
3891     // return the CSS percentage RGBA Color value
3892     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3893
3894   },
3895
3896   /**
3897    * getCSSHSL
3898    * @return {String} a string representing this Color as a CSS HSL Color value - that
3899    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3900    * s and l are in the range [0,100].
3901    */
3902   getCSSHSL : function(){
3903
3904     // get the HSL components
3905     var hsl = this.getHSL();
3906
3907     // return the CSS HSL Color value
3908     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3909
3910   },
3911
3912   /**
3913    * getCSSHSLA
3914    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3915    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3916    * s and l are in the range [0,100], and a is in the range [0,1].
3917    */
3918   getCSSHSLA : function(){
3919
3920     // get the HSL components
3921     var hsl = this.getHSL();
3922
3923     // return the CSS HSL Color value
3924     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3925
3926   },
3927
3928   /**
3929    * Sets the Color of the specified node to this Color. This functions sets
3930    * the CSS 'color' property for the node. The parameter is:
3931    * 
3932    * @param {DomElement} node - the node whose Color should be set
3933    */
3934   setNodeColor : function(node){
3935
3936     // set the Color of the node
3937     node.style.color = this.getCSSHexadecimalRGB();
3938
3939   },
3940
3941   /**
3942    * Sets the background Color of the specified node to this Color. This
3943    * functions sets the CSS 'background-color' property for the node. The
3944    * parameter is:
3945    *
3946    * @param {DomElement} node - the node whose background Color should be set
3947    */
3948   setNodeBackgroundColor : function(node){
3949
3950     // set the background Color of the node
3951     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3952
3953   },
3954   // convert between formats..
3955   toRGB: function()
3956   {
3957     var r = this.getIntegerRGB();
3958     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3959     
3960   },
3961   toHSL : function()
3962   {
3963      var hsl = this.getHSL();
3964   // return the CSS HSL Color value
3965     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3966     
3967   },
3968   
3969   toHSV : function()
3970   {
3971     var rgb = this.toRGB();
3972     var hsv = rgb.getHSV();
3973    // return the CSS HSL Color value
3974     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3975     
3976   },
3977   
3978   // modify  v = 0 ... 1 (eg. 0.5)
3979   saturate : function(v)
3980   {
3981       var rgb = this.toRGB();
3982       var hsv = rgb.getHSV();
3983       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3984       
3985   
3986   },
3987   
3988    
3989   /**
3990    * getRGB
3991    * @return {Object} the RGB and alpha components of this Color as an object with r,
3992    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3993    * the range [0,1].
3994    */
3995   getRGB: function(){
3996    
3997     // return the RGB components
3998     return {
3999       'r' : this.rgb.r,
4000       'g' : this.rgb.g,
4001       'b' : this.rgb.b,
4002       'a' : this.alpha
4003     };
4004
4005   },
4006
4007   /**
4008    * getHSV
4009    * @return {Object} the HSV and alpha components of this Color as an object with h,
4010    * s, v, and a properties. h is in the range [0,360), s and v are in the range
4011    * [0,100], and a is in the range [0,1].
4012    */
4013   getHSV : function()
4014   {
4015     
4016     // calculate the HSV components if necessary
4017     if (this.hsv == null) {
4018       this.calculateHSV();
4019     }
4020
4021     // return the HSV components
4022     return {
4023       'h' : this.hsv.h,
4024       's' : this.hsv.s,
4025       'v' : this.hsv.v,
4026       'a' : this.alpha
4027     };
4028
4029   },
4030
4031   /**
4032    * getHSL
4033    * @return {Object} the HSL and alpha components of this Color as an object with h,
4034    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4035    * [0,100], and a is in the range [0,1].
4036    */
4037   getHSL : function(){
4038     
4039      
4040     // calculate the HSV components if necessary
4041     if (this.hsl == null) { this.calculateHSL(); }
4042
4043     // return the HSL components
4044     return {
4045       'h' : this.hsl.h,
4046       's' : this.hsl.s,
4047       'l' : this.hsl.l,
4048       'a' : this.alpha
4049     };
4050
4051   }
4052   
4053
4054 });
4055
4056
4057 /**
4058  * @class Roo.lib.RGBColor
4059  * @extends Roo.lib.Color
4060  * Creates a Color specified in the RGB Color space, with an optional alpha
4061  * component. The parameters are:
4062  * @constructor
4063  * 
4064
4065  * @param {Number} r - the red component, clipped to the range [0,255]
4066  * @param {Number} g - the green component, clipped to the range [0,255]
4067  * @param {Number} b - the blue component, clipped to the range [0,255]
4068  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4069  *     optional and defaults to 1
4070  */
4071 Roo.lib.RGBColor = function (r, g, b, a){
4072
4073   // store the alpha component after clipping it if necessary
4074   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4075
4076   // store the RGB components after clipping them if necessary
4077   this.rgb =
4078       {
4079         'r' : Math.max(0, Math.min(255, r)),
4080         'g' : Math.max(0, Math.min(255, g)),
4081         'b' : Math.max(0, Math.min(255, b))
4082       };
4083
4084   // initialise the HSV and HSL components to null
4085   
4086
4087   /* 
4088    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4089    * range [0,360). The parameters are:
4090    *
4091    * maximum - the maximum of the RGB component values
4092    * range   - the range of the RGB component values
4093    */
4094    
4095
4096 }
4097 // this does an 'exteds'
4098 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4099
4100   
4101     getHue  : function(maximum, range)
4102     {
4103       var rgb = this.rgb;
4104        
4105       // check whether the range is zero
4106       if (range == 0){
4107   
4108         // set the hue to zero (any hue is acceptable as the Color is grey)
4109         var hue = 0;
4110   
4111       }else{
4112   
4113         // determine which of the components has the highest value and set the hue
4114         switch (maximum){
4115   
4116           // red has the highest value
4117           case rgb.r:
4118             var hue = (rgb.g - rgb.b) / range * 60;
4119             if (hue < 0) { hue += 360; }
4120             break;
4121   
4122           // green has the highest value
4123           case rgb.g:
4124             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4125             break;
4126   
4127           // blue has the highest value
4128           case rgb.b:
4129             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4130             break;
4131   
4132         }
4133   
4134       }
4135   
4136       // return the hue
4137       return hue;
4138   
4139     },
4140
4141   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4142    * be returned be the getHSV function.
4143    */
4144    calculateHSV : function(){
4145     var rgb = this.rgb;
4146     // get the maximum and range of the RGB component values
4147     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4148     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4149
4150     // store the HSV components
4151     this.hsv =
4152         {
4153           'h' : this.getHue(maximum, range),
4154           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4155           'v' : maximum / 2.55
4156         };
4157
4158   },
4159
4160   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4161    * be returned be the getHSL function.
4162    */
4163    calculateHSL : function(){
4164     var rgb = this.rgb;
4165     // get the maximum and range of the RGB component values
4166     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4167     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4168
4169     // determine the lightness in the range [0,1]
4170     var l = maximum / 255 - range / 510;
4171
4172     // store the HSL components
4173     this.hsl =
4174         {
4175           'h' : this.getHue(maximum, range),
4176           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4177           'l' : 100 * l
4178         };
4179
4180   }
4181
4182 });
4183
4184 /**
4185  * @class Roo.lib.HSVColor
4186  * @extends Roo.lib.Color
4187  * Creates a Color specified in the HSV Color space, with an optional alpha
4188  * component. The parameters are:
4189  * @constructor
4190  *
4191  * @param {Number} h - the hue component, wrapped to the range [0,360)
4192  * @param {Number} s - the saturation component, clipped to the range [0,100]
4193  * @param {Number} v - the value component, clipped to the range [0,100]
4194  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4195  *     optional and defaults to 1
4196  */
4197 Roo.lib.HSVColor = function (h, s, v, a){
4198
4199   // store the alpha component after clipping it if necessary
4200   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4201
4202   // store the HSV components after clipping or wrapping them if necessary
4203   this.hsv =
4204       {
4205         'h' : (h % 360 + 360) % 360,
4206         's' : Math.max(0, Math.min(100, s)),
4207         'v' : Math.max(0, Math.min(100, v))
4208       };
4209
4210   // initialise the RGB and HSL components to null
4211   this.rgb = null;
4212   this.hsl = null;
4213 }
4214
4215 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4216   /* Calculates and stores the RGB components of this HSVColor so that they can
4217    * be returned be the getRGB function.
4218    */
4219   calculateRGB: function ()
4220   {
4221     var hsv = this.hsv;
4222     // check whether the saturation is zero
4223     if (hsv.s == 0){
4224
4225       // set the Color to the appropriate shade of grey
4226       var r = hsv.v;
4227       var g = hsv.v;
4228       var b = hsv.v;
4229
4230     }else{
4231
4232       // set some temporary values
4233       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4234       var p  = hsv.v * (1 - hsv.s / 100);
4235       var q  = hsv.v * (1 - hsv.s / 100 * f);
4236       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4237
4238       // set the RGB Color components to their temporary values
4239       switch (Math.floor(hsv.h / 60)){
4240         case 0: var r = hsv.v; var g = t; var b = p; break;
4241         case 1: var r = q; var g = hsv.v; var b = p; break;
4242         case 2: var r = p; var g = hsv.v; var b = t; break;
4243         case 3: var r = p; var g = q; var b = hsv.v; break;
4244         case 4: var r = t; var g = p; var b = hsv.v; break;
4245         case 5: var r = hsv.v; var g = p; var b = q; break;
4246       }
4247
4248     }
4249
4250     // store the RGB components
4251     this.rgb =
4252         {
4253           'r' : r * 2.55,
4254           'g' : g * 2.55,
4255           'b' : b * 2.55
4256         };
4257
4258   },
4259
4260   /* Calculates and stores the HSL components of this HSVColor so that they can
4261    * be returned be the getHSL function.
4262    */
4263   calculateHSL : function (){
4264
4265     var hsv = this.hsv;
4266     // determine the lightness in the range [0,100]
4267     var l = (2 - hsv.s / 100) * hsv.v / 2;
4268
4269     // store the HSL components
4270     this.hsl =
4271         {
4272           'h' : hsv.h,
4273           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4274           'l' : l
4275         };
4276
4277     // correct a division-by-zero error
4278     if (isNaN(hsl.s)) { hsl.s = 0; }
4279
4280   } 
4281  
4282
4283 });
4284  
4285
4286 /**
4287  * @class Roo.lib.HSLColor
4288  * @extends Roo.lib.Color
4289  *
4290  * @constructor
4291  * Creates a Color specified in the HSL Color space, with an optional alpha
4292  * component. The parameters are:
4293  *
4294  * @param {Number} h - the hue component, wrapped to the range [0,360)
4295  * @param {Number} s - the saturation component, clipped to the range [0,100]
4296  * @param {Number} l - the lightness component, clipped to the range [0,100]
4297  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4298  *     optional and defaults to 1
4299  */
4300
4301 Roo.lib.HSLColor = function(h, s, l, a){
4302
4303   // store the alpha component after clipping it if necessary
4304   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4305
4306   // store the HSL components after clipping or wrapping them if necessary
4307   this.hsl =
4308       {
4309         'h' : (h % 360 + 360) % 360,
4310         's' : Math.max(0, Math.min(100, s)),
4311         'l' : Math.max(0, Math.min(100, l))
4312       };
4313
4314   // initialise the RGB and HSV components to null
4315 }
4316
4317 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4318
4319   /* Calculates and stores the RGB components of this HSLColor so that they can
4320    * be returned be the getRGB function.
4321    */
4322   calculateRGB: function (){
4323
4324     // check whether the saturation is zero
4325     if (this.hsl.s == 0){
4326
4327       // store the RGB components representing the appropriate shade of grey
4328       this.rgb =
4329           {
4330             'r' : this.hsl.l * 2.55,
4331             'g' : this.hsl.l * 2.55,
4332             'b' : this.hsl.l * 2.55
4333           };
4334
4335     }else{
4336
4337       // set some temporary values
4338       var p = this.hsl.l < 50
4339             ? this.hsl.l * (1 + hsl.s / 100)
4340             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4341       var q = 2 * hsl.l - p;
4342
4343       // initialise the RGB components
4344       this.rgb =
4345           {
4346             'r' : (h + 120) / 60 % 6,
4347             'g' : h / 60,
4348             'b' : (h + 240) / 60 % 6
4349           };
4350
4351       // loop over the RGB components
4352       for (var key in this.rgb){
4353
4354         // ensure that the property is not inherited from the root object
4355         if (this.rgb.hasOwnProperty(key)){
4356
4357           // set the component to its value in the range [0,100]
4358           if (this.rgb[key] < 1){
4359             this.rgb[key] = q + (p - q) * this.rgb[key];
4360           }else if (this.rgb[key] < 3){
4361             this.rgb[key] = p;
4362           }else if (this.rgb[key] < 4){
4363             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4364           }else{
4365             this.rgb[key] = q;
4366           }
4367
4368           // set the component to its value in the range [0,255]
4369           this.rgb[key] *= 2.55;
4370
4371         }
4372
4373       }
4374
4375     }
4376
4377   },
4378
4379   /* Calculates and stores the HSV components of this HSLColor so that they can
4380    * be returned be the getHSL function.
4381    */
4382    calculateHSV : function(){
4383
4384     // set a temporary value
4385     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4386
4387     // store the HSV components
4388     this.hsv =
4389         {
4390           'h' : this.hsl.h,
4391           's' : 200 * t / (this.hsl.l + t),
4392           'v' : t + this.hsl.l
4393         };
4394
4395     // correct a division-by-zero error
4396     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4397
4398   }
4399  
4400
4401 });
4402 /*
4403  * Portions of this file are based on pieces of Yahoo User Interface Library
4404  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4405  * YUI licensed under the BSD License:
4406  * http://developer.yahoo.net/yui/license.txt
4407  * <script type="text/javascript">
4408  *
4409  */
4410 (function() {
4411
4412     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4413         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4414     };
4415
4416     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4417
4418     var fly = Roo.lib.AnimBase.fly;
4419     var Y = Roo.lib;
4420     var superclass = Y.ColorAnim.superclass;
4421     var proto = Y.ColorAnim.prototype;
4422
4423     proto.toString = function() {
4424         var el = this.getEl();
4425         var id = el.id || el.tagName;
4426         return ("ColorAnim " + id);
4427     };
4428
4429     proto.patterns.color = /color$/i;
4430     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4431     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4432     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4433     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4434
4435
4436     proto.parseColor = function(s) {
4437         if (s.length == 3) {
4438             return s;
4439         }
4440
4441         var c = this.patterns.hex.exec(s);
4442         if (c && c.length == 4) {
4443             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4444         }
4445
4446         c = this.patterns.rgb.exec(s);
4447         if (c && c.length == 4) {
4448             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4449         }
4450
4451         c = this.patterns.hex3.exec(s);
4452         if (c && c.length == 4) {
4453             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4454         }
4455
4456         return null;
4457     };
4458     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4459     proto.getAttribute = function(attr) {
4460         var el = this.getEl();
4461         if (this.patterns.color.test(attr)) {
4462             var val = fly(el).getStyle(attr);
4463
4464             if (this.patterns.transparent.test(val)) {
4465                 var parent = el.parentNode;
4466                 val = fly(parent).getStyle(attr);
4467
4468                 while (parent && this.patterns.transparent.test(val)) {
4469                     parent = parent.parentNode;
4470                     val = fly(parent).getStyle(attr);
4471                     if (parent.tagName.toUpperCase() == 'HTML') {
4472                         val = '#fff';
4473                     }
4474                 }
4475             }
4476         } else {
4477             val = superclass.getAttribute.call(this, attr);
4478         }
4479
4480         return val;
4481     };
4482     proto.getAttribute = function(attr) {
4483         var el = this.getEl();
4484         if (this.patterns.color.test(attr)) {
4485             var val = fly(el).getStyle(attr);
4486
4487             if (this.patterns.transparent.test(val)) {
4488                 var parent = el.parentNode;
4489                 val = fly(parent).getStyle(attr);
4490
4491                 while (parent && this.patterns.transparent.test(val)) {
4492                     parent = parent.parentNode;
4493                     val = fly(parent).getStyle(attr);
4494                     if (parent.tagName.toUpperCase() == 'HTML') {
4495                         val = '#fff';
4496                     }
4497                 }
4498             }
4499         } else {
4500             val = superclass.getAttribute.call(this, attr);
4501         }
4502
4503         return val;
4504     };
4505
4506     proto.doMethod = function(attr, start, end) {
4507         var val;
4508
4509         if (this.patterns.color.test(attr)) {
4510             val = [];
4511             for (var i = 0, len = start.length; i < len; ++i) {
4512                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4513             }
4514
4515             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4516         }
4517         else {
4518             val = superclass.doMethod.call(this, attr, start, end);
4519         }
4520
4521         return val;
4522     };
4523
4524     proto.setRuntimeAttribute = function(attr) {
4525         superclass.setRuntimeAttribute.call(this, attr);
4526
4527         if (this.patterns.color.test(attr)) {
4528             var attributes = this.attributes;
4529             var start = this.parseColor(this.runtimeAttributes[attr].start);
4530             var end = this.parseColor(this.runtimeAttributes[attr].end);
4531
4532             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4533                 end = this.parseColor(attributes[attr].by);
4534
4535                 for (var i = 0, len = start.length; i < len; ++i) {
4536                     end[i] = start[i] + end[i];
4537                 }
4538             }
4539
4540             this.runtimeAttributes[attr].start = start;
4541             this.runtimeAttributes[attr].end = end;
4542         }
4543     };
4544 })();
4545
4546 /*
4547  * Portions of this file are based on pieces of Yahoo User Interface Library
4548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4549  * YUI licensed under the BSD License:
4550  * http://developer.yahoo.net/yui/license.txt
4551  * <script type="text/javascript">
4552  *
4553  */
4554 Roo.lib.Easing = {
4555
4556
4557     easeNone: function (t, b, c, d) {
4558         return c * t / d + b;
4559     },
4560
4561
4562     easeIn: function (t, b, c, d) {
4563         return c * (t /= d) * t + b;
4564     },
4565
4566
4567     easeOut: function (t, b, c, d) {
4568         return -c * (t /= d) * (t - 2) + b;
4569     },
4570
4571
4572     easeBoth: function (t, b, c, d) {
4573         if ((t /= d / 2) < 1) {
4574             return c / 2 * t * t + b;
4575         }
4576
4577         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4578     },
4579
4580
4581     easeInStrong: function (t, b, c, d) {
4582         return c * (t /= d) * t * t * t + b;
4583     },
4584
4585
4586     easeOutStrong: function (t, b, c, d) {
4587         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4588     },
4589
4590
4591     easeBothStrong: function (t, b, c, d) {
4592         if ((t /= d / 2) < 1) {
4593             return c / 2 * t * t * t * t + b;
4594         }
4595
4596         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4597     },
4598
4599
4600
4601     elasticIn: function (t, b, c, d, a, p) {
4602         if (t == 0) {
4603             return b;
4604         }
4605         if ((t /= d) == 1) {
4606             return b + c;
4607         }
4608         if (!p) {
4609             p = d * .3;
4610         }
4611
4612         if (!a || a < Math.abs(c)) {
4613             a = c;
4614             var s = p / 4;
4615         }
4616         else {
4617             var s = p / (2 * Math.PI) * Math.asin(c / a);
4618         }
4619
4620         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4621     },
4622
4623
4624     elasticOut: function (t, b, c, d, a, p) {
4625         if (t == 0) {
4626             return b;
4627         }
4628         if ((t /= d) == 1) {
4629             return b + c;
4630         }
4631         if (!p) {
4632             p = d * .3;
4633         }
4634
4635         if (!a || a < Math.abs(c)) {
4636             a = c;
4637             var s = p / 4;
4638         }
4639         else {
4640             var s = p / (2 * Math.PI) * Math.asin(c / a);
4641         }
4642
4643         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4644     },
4645
4646
4647     elasticBoth: function (t, b, c, d, a, p) {
4648         if (t == 0) {
4649             return b;
4650         }
4651
4652         if ((t /= d / 2) == 2) {
4653             return b + c;
4654         }
4655
4656         if (!p) {
4657             p = d * (.3 * 1.5);
4658         }
4659
4660         if (!a || a < Math.abs(c)) {
4661             a = c;
4662             var s = p / 4;
4663         }
4664         else {
4665             var s = p / (2 * Math.PI) * Math.asin(c / a);
4666         }
4667
4668         if (t < 1) {
4669             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4670                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4671         }
4672         return a * Math.pow(2, -10 * (t -= 1)) *
4673                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4674     },
4675
4676
4677
4678     backIn: function (t, b, c, d, s) {
4679         if (typeof s == 'undefined') {
4680             s = 1.70158;
4681         }
4682         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4683     },
4684
4685
4686     backOut: function (t, b, c, d, s) {
4687         if (typeof s == 'undefined') {
4688             s = 1.70158;
4689         }
4690         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4691     },
4692
4693
4694     backBoth: function (t, b, c, d, s) {
4695         if (typeof s == 'undefined') {
4696             s = 1.70158;
4697         }
4698
4699         if ((t /= d / 2 ) < 1) {
4700             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4701         }
4702         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4703     },
4704
4705
4706     bounceIn: function (t, b, c, d) {
4707         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4708     },
4709
4710
4711     bounceOut: function (t, b, c, d) {
4712         if ((t /= d) < (1 / 2.75)) {
4713             return c * (7.5625 * t * t) + b;
4714         } else if (t < (2 / 2.75)) {
4715             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4716         } else if (t < (2.5 / 2.75)) {
4717             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4718         }
4719         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4720     },
4721
4722
4723     bounceBoth: function (t, b, c, d) {
4724         if (t < d / 2) {
4725             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4726         }
4727         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4728     }
4729 };/*
4730  * Portions of this file are based on pieces of Yahoo User Interface Library
4731  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4732  * YUI licensed under the BSD License:
4733  * http://developer.yahoo.net/yui/license.txt
4734  * <script type="text/javascript">
4735  *
4736  */
4737     (function() {
4738         Roo.lib.Motion = function(el, attributes, duration, method) {
4739             if (el) {
4740                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4741             }
4742         };
4743
4744         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4745
4746
4747         var Y = Roo.lib;
4748         var superclass = Y.Motion.superclass;
4749         var proto = Y.Motion.prototype;
4750
4751         proto.toString = function() {
4752             var el = this.getEl();
4753             var id = el.id || el.tagName;
4754             return ("Motion " + id);
4755         };
4756
4757         proto.patterns.points = /^points$/i;
4758
4759         proto.setAttribute = function(attr, val, unit) {
4760             if (this.patterns.points.test(attr)) {
4761                 unit = unit || 'px';
4762                 superclass.setAttribute.call(this, 'left', val[0], unit);
4763                 superclass.setAttribute.call(this, 'top', val[1], unit);
4764             } else {
4765                 superclass.setAttribute.call(this, attr, val, unit);
4766             }
4767         };
4768
4769         proto.getAttribute = function(attr) {
4770             if (this.patterns.points.test(attr)) {
4771                 var val = [
4772                         superclass.getAttribute.call(this, 'left'),
4773                         superclass.getAttribute.call(this, 'top')
4774                         ];
4775             } else {
4776                 val = superclass.getAttribute.call(this, attr);
4777             }
4778
4779             return val;
4780         };
4781
4782         proto.doMethod = function(attr, start, end) {
4783             var val = null;
4784
4785             if (this.patterns.points.test(attr)) {
4786                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4787                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4788             } else {
4789                 val = superclass.doMethod.call(this, attr, start, end);
4790             }
4791             return val;
4792         };
4793
4794         proto.setRuntimeAttribute = function(attr) {
4795             if (this.patterns.points.test(attr)) {
4796                 var el = this.getEl();
4797                 var attributes = this.attributes;
4798                 var start;
4799                 var control = attributes['points']['control'] || [];
4800                 var end;
4801                 var i, len;
4802
4803                 if (control.length > 0 && !(control[0] instanceof Array)) {
4804                     control = [control];
4805                 } else {
4806                     var tmp = [];
4807                     for (i = 0,len = control.length; i < len; ++i) {
4808                         tmp[i] = control[i];
4809                     }
4810                     control = tmp;
4811                 }
4812
4813                 Roo.fly(el).position();
4814
4815                 if (isset(attributes['points']['from'])) {
4816                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4817                 }
4818                 else {
4819                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4820                 }
4821
4822                 start = this.getAttribute('points');
4823
4824
4825                 if (isset(attributes['points']['to'])) {
4826                     end = translateValues.call(this, attributes['points']['to'], start);
4827
4828                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4829                     for (i = 0,len = control.length; i < len; ++i) {
4830                         control[i] = translateValues.call(this, control[i], start);
4831                     }
4832
4833
4834                 } else if (isset(attributes['points']['by'])) {
4835                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4836
4837                     for (i = 0,len = control.length; i < len; ++i) {
4838                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4839                     }
4840                 }
4841
4842                 this.runtimeAttributes[attr] = [start];
4843
4844                 if (control.length > 0) {
4845                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4846                 }
4847
4848                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4849             }
4850             else {
4851                 superclass.setRuntimeAttribute.call(this, attr);
4852             }
4853         };
4854
4855         var translateValues = function(val, start) {
4856             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4857             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4858
4859             return val;
4860         };
4861
4862         var isset = function(prop) {
4863             return (typeof prop !== 'undefined');
4864         };
4865     })();
4866 /*
4867  * Portions of this file are based on pieces of Yahoo User Interface Library
4868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4869  * YUI licensed under the BSD License:
4870  * http://developer.yahoo.net/yui/license.txt
4871  * <script type="text/javascript">
4872  *
4873  */
4874     (function() {
4875         Roo.lib.Scroll = function(el, attributes, duration, method) {
4876             if (el) {
4877                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4878             }
4879         };
4880
4881         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4882
4883
4884         var Y = Roo.lib;
4885         var superclass = Y.Scroll.superclass;
4886         var proto = Y.Scroll.prototype;
4887
4888         proto.toString = function() {
4889             var el = this.getEl();
4890             var id = el.id || el.tagName;
4891             return ("Scroll " + id);
4892         };
4893
4894         proto.doMethod = function(attr, start, end) {
4895             var val = null;
4896
4897             if (attr == 'scroll') {
4898                 val = [
4899                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4900                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4901                         ];
4902
4903             } else {
4904                 val = superclass.doMethod.call(this, attr, start, end);
4905             }
4906             return val;
4907         };
4908
4909         proto.getAttribute = function(attr) {
4910             var val = null;
4911             var el = this.getEl();
4912
4913             if (attr == 'scroll') {
4914                 val = [ el.scrollLeft, el.scrollTop ];
4915             } else {
4916                 val = superclass.getAttribute.call(this, attr);
4917             }
4918
4919             return val;
4920         };
4921
4922         proto.setAttribute = function(attr, val, unit) {
4923             var el = this.getEl();
4924
4925             if (attr == 'scroll') {
4926                 el.scrollLeft = val[0];
4927                 el.scrollTop = val[1];
4928             } else {
4929                 superclass.setAttribute.call(this, attr, val, unit);
4930             }
4931         };
4932     })();
4933 /**
4934  * Originally based of this code... - refactored for Roo...
4935  * https://github.com/aaalsaleh/undo-manager
4936  
4937  * undo-manager.js
4938  * @author  Abdulrahman Alsaleh 
4939  * @copyright 2015 Abdulrahman Alsaleh 
4940  * @license  MIT License (c) 
4941  *
4942  * Hackily modifyed by alan@roojs.com
4943  *
4944  *
4945  *  
4946  *
4947  *  TOTALLY UNTESTED...
4948  *
4949  *  Documentation to be done....
4950  */
4951  
4952
4953 /**
4954 * @class Roo.lib.UndoManager
4955 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4956 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4957
4958  * Usage:
4959  * <pre><code>
4960
4961
4962 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4963  
4964 </code></pre>
4965
4966 * For more information see this blog post with examples:
4967 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4968      - Create Elements using DOM, HTML fragments and Templates</a>. 
4969 * @constructor
4970 * @param {Number} limit how far back to go ... use 1000?
4971 * @param {Object} scope usually use document..
4972 */
4973
4974 Roo.lib.UndoManager = function (limit, undoScopeHost)
4975 {
4976     this.stack = [];
4977     this.limit = limit;
4978     this.scope = undoScopeHost;
4979     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4980     if (this.fireEvent) {
4981         this.bindEvents();
4982     }
4983     this.reset();
4984     
4985 };
4986         
4987 Roo.lib.UndoManager.prototype = {
4988     
4989     limit : false,
4990     stack : false,
4991     scope :  false,
4992     fireEvent : false,
4993     position : 0,
4994     length : 0,
4995     
4996     
4997      /**
4998      * To push and execute a transaction, the method undoManager.transact
4999      * must be called by passing a transaction object as the first argument, and a merge
5000      * flag as the second argument. A transaction object has the following properties:
5001      *
5002      * Usage:
5003 <pre><code>
5004 undoManager.transact({
5005     label: 'Typing',
5006     execute: function() { ... },
5007     undo: function() { ... },
5008     // redo same as execute
5009     redo: function() { this.execute(); }
5010 }, false);
5011
5012 // merge transaction
5013 undoManager.transact({
5014     label: 'Typing',
5015     execute: function() { ... },  // this will be run...
5016     undo: function() { ... }, // what to do when undo is run.
5017     // redo same as execute
5018     redo: function() { this.execute(); }
5019 }, true); 
5020 </code></pre> 
5021      *
5022      * 
5023      * @param {Object} transaction The transaction to add to the stack.
5024      * @return {String} The HTML fragment
5025      */
5026     
5027     
5028     transact : function (transaction, merge)
5029     {
5030         if (arguments.length < 2) {
5031             throw new TypeError('Not enough arguments to UndoManager.transact.');
5032         }
5033
5034         transaction.execute();
5035
5036         this.stack.splice(0, this.position);
5037         if (merge && this.length) {
5038             this.stack[0].push(transaction);
5039         } else {
5040             this.stack.unshift([transaction]);
5041         }
5042     
5043         this.position = 0;
5044
5045         if (this.limit && this.stack.length > this.limit) {
5046             this.length = this.stack.length = this.limit;
5047         } else {
5048             this.length = this.stack.length;
5049         }
5050
5051         if (this.fireEvent) {
5052             this.scope.dispatchEvent(
5053                 new CustomEvent('DOMTransaction', {
5054                     detail: {
5055                         transactions: this.stack[0].slice()
5056                     },
5057                     bubbles: true,
5058                     cancelable: false
5059                 })
5060             );
5061         }
5062         
5063         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5064       
5065         
5066     },
5067
5068     undo : function ()
5069     {
5070         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5071         
5072         if (this.position < this.length) {
5073             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5074                 this.stack[this.position][i].undo();
5075             }
5076             this.position++;
5077
5078             if (this.fireEvent) {
5079                 this.scope.dispatchEvent(
5080                     new CustomEvent('undo', {
5081                         detail: {
5082                             transactions: this.stack[this.position - 1].slice()
5083                         },
5084                         bubbles: true,
5085                         cancelable: false
5086                     })
5087                 );
5088             }
5089         }
5090     },
5091
5092     redo : function ()
5093     {
5094         if (this.position > 0) {
5095             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5096                 this.stack[this.position - 1][i].redo();
5097             }
5098             this.position--;
5099
5100             if (this.fireEvent) {
5101                 this.scope.dispatchEvent(
5102                     new CustomEvent('redo', {
5103                         detail: {
5104                             transactions: this.stack[this.position].slice()
5105                         },
5106                         bubbles: true,
5107                         cancelable: false
5108                     })
5109                 );
5110             }
5111         }
5112     },
5113
5114     item : function (index)
5115     {
5116         if (index >= 0 && index < this.length) {
5117             return this.stack[index].slice();
5118         }
5119         return null;
5120     },
5121
5122     clearUndo : function () {
5123         this.stack.length = this.length = this.position;
5124     },
5125
5126     clearRedo : function () {
5127         this.stack.splice(0, this.position);
5128         this.position = 0;
5129         this.length = this.stack.length;
5130     },
5131     /**
5132      * Reset the undo - probaly done on load to clear all history.
5133      */
5134     reset : function()
5135     {
5136         this.stack = [];
5137         this.position = 0;
5138         this.length = 0;
5139         this.current_html = this.scope.innerHTML;
5140         if (this.timer !== false) {
5141             clearTimeout(this.timer);
5142         }
5143         this.timer = false;
5144         this.merge = false;
5145         this.addEvent();
5146         
5147     },
5148     current_html : '',
5149     timer : false,
5150     merge : false,
5151     
5152     
5153     // this will handle the undo/redo on the element.?
5154     bindEvents : function()
5155     {
5156         var el  = this.scope;
5157         el.undoManager = this;
5158         
5159         
5160         this.scope.addEventListener('keydown', function(e) {
5161             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5162                 if (e.shiftKey) {
5163                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5164                 } else {
5165                     el.undoManager.undo(); // Ctrl/Command + Z
5166                 }
5167         
5168                 e.preventDefault();
5169             }
5170         });
5171         /// ignore keyup..
5172         this.scope.addEventListener('keyup', function(e) {
5173             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5174                 e.preventDefault();
5175             }
5176         });
5177         
5178         
5179         
5180         var t = this;
5181         
5182         el.addEventListener('input', function(e) {
5183             if(el.innerHTML == t.current_html) {
5184                 return;
5185             }
5186             // only record events every second.
5187             if (t.timer !== false) {
5188                clearTimeout(t.timer);
5189                t.timer = false;
5190             }
5191             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5192             
5193             t.addEvent(t.merge);
5194             t.merge = true; // ignore changes happening every second..
5195         });
5196         },
5197     /**
5198      * Manually add an event.
5199      * Normall called without arguements - and it will just get added to the stack.
5200      * 
5201      */
5202     
5203     addEvent : function(merge)
5204     {
5205         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5206         // not sure if this should clear the timer 
5207         merge = typeof(merge) == 'undefined' ? false : merge; 
5208         
5209         this.scope.undoManager.transact({
5210             scope : this.scope,
5211             oldHTML: this.current_html,
5212             newHTML: this.scope.innerHTML,
5213             // nothing to execute (content already changed when input is fired)
5214             execute: function() { },
5215             undo: function() {
5216                 this.scope.innerHTML = this.current_html = this.oldHTML;
5217             },
5218             redo: function() {
5219                 this.scope.innerHTML = this.current_html = this.newHTML;
5220             }
5221         }, false); //merge);
5222         
5223         this.merge = merge;
5224         
5225         this.current_html = this.scope.innerHTML;
5226     }
5227     
5228     
5229      
5230     
5231     
5232     
5233 };
5234 /**
5235  * @class Roo.lib.Range
5236  * @constructor
5237  * This is a toolkit, normally used to copy features into a Dom Range element
5238  * Roo.lib.Range.wrap(x);
5239  *
5240  *
5241  *
5242  */
5243 Roo.lib.Range = function() { };
5244
5245 /**
5246  * Wrap a Dom Range object, to give it new features...
5247  * @static
5248  * @param {Range} the range to wrap
5249  */
5250 Roo.lib.Range.wrap = function(r) {
5251     return Roo.apply(r, Roo.lib.Range.prototype);
5252 };
5253 /**
5254  * find a parent node eg. LI / OL
5255  * @param {string|Array} node name or array of nodenames
5256  * @return {DomElement|false}
5257  */
5258 Roo.apply(Roo.lib.Range.prototype,
5259 {
5260     
5261     closest : function(str)
5262     {
5263         if (typeof(str) != 'string') {
5264             // assume it's a array.
5265             for(var i = 0;i < str.length;i++) {
5266                 var r = this.closest(str[i]);
5267                 if (r !== false) {
5268                     return r;
5269                 }
5270                 
5271             }
5272             return false;
5273         }
5274         str = str.toLowerCase();
5275         var n = this.commonAncestorContainer; // might not be a node
5276         while (n.nodeType != 1) {
5277             n = n.parentNode;
5278         }
5279         
5280         if (n.nodeName.toLowerCase() == str ) {
5281             return n;
5282         }
5283         if (n.nodeName.toLowerCase() == 'body') {
5284             return false;
5285         }
5286             
5287         return n.closest(str) || false;
5288         
5289     },
5290     cloneRange : function()
5291     {
5292         return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5293     }
5294 });/**
5295  * @class Roo.lib.Selection
5296  * @constructor
5297  * This is a toolkit, normally used to copy features into a Dom Selection element
5298  * Roo.lib.Selection.wrap(x);
5299  *
5300  *
5301  *
5302  */
5303 Roo.lib.Selection = function() { };
5304
5305 /**
5306  * Wrap a Dom Range object, to give it new features...
5307  * @static
5308  * @param {Range} the range to wrap
5309  */
5310 Roo.lib.Selection.wrap = function(r, doc) {
5311     Roo.apply(r, Roo.lib.Selection.prototype);
5312     r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5313     return r;
5314 };
5315 /**
5316  * find a parent node eg. LI / OL
5317  * @param {string|Array} node name or array of nodenames
5318  * @return {DomElement|false}
5319  */
5320 Roo.apply(Roo.lib.Selection.prototype,
5321 {
5322     /**
5323      * the owner document
5324      */
5325     ownerDocument : false,
5326     
5327     getRangeAt : function(n)
5328     {
5329         return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5330     },
5331     
5332     /**
5333      * insert node at selection 
5334      * @param {DomElement|string} node
5335      * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5336      */
5337     insertNode: function(node, cursor)
5338     {
5339         if (typeof(node) == 'string') {
5340             node = this.ownerDocument.createElement(node);
5341             if (cursor == 'in') {
5342                 node.innerHTML = '&nbsp;';
5343             }
5344         }
5345         
5346         var range = this.getRangeAt(0);
5347         
5348         if (this.type != 'Caret') {
5349             range.deleteContents();
5350         }
5351         var sn = node.childNodes[0]; // select the contents.
5352
5353         
5354         
5355         range.insertNode(node);
5356         if (cursor == 'after') {
5357             node.insertAdjacentHTML('afterend', '&nbsp;');
5358             sn = node.nextSibling;
5359         }
5360         
5361         if (cursor == 'none') {
5362             return;
5363         }
5364         
5365         this.cursorText(sn);
5366     },
5367     
5368     cursorText : function(n)
5369     {
5370        
5371         //var range = this.getRangeAt(0);
5372         range = Roo.lib.Range.wrap(new Range());
5373         //range.selectNode(n);
5374         
5375         var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5376         range.setStart(n.parentNode,ix);
5377         range.setEnd(n.parentNode,ix+1);
5378         //range.collapse(false);
5379          
5380         this.removeAllRanges();
5381         this.addRange(range);
5382         
5383         Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5384     },
5385     cursorAfter : function(n)
5386     {
5387         if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
5388             n.insertAdjacentHTML('afterend', '&nbsp;');
5389         }
5390         this.cursorText (n.nextSibling);
5391     }
5392         
5393     
5394 });/*
5395  * Based on:
5396  * Ext JS Library 1.1.1
5397  * Copyright(c) 2006-2007, Ext JS, LLC.
5398  *
5399  * Originally Released Under LGPL - original licence link has changed is not relivant.
5400  *
5401  * Fork - LGPL
5402  * <script type="text/javascript">
5403  */
5404
5405
5406 // nasty IE9 hack - what a pile of crap that is..
5407
5408  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5409     Range.prototype.createContextualFragment = function (html) {
5410         var doc = window.document;
5411         var container = doc.createElement("div");
5412         container.innerHTML = html;
5413         var frag = doc.createDocumentFragment(), n;
5414         while ((n = container.firstChild)) {
5415             frag.appendChild(n);
5416         }
5417         return frag;
5418     };
5419 }
5420
5421 /**
5422  * @class Roo.DomHelper
5423  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5424  * 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>.
5425  * @static
5426  */
5427 Roo.DomHelper = function(){
5428     var tempTableEl = null;
5429     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5430     var tableRe = /^table|tbody|tr|td$/i;
5431     var xmlns = {};
5432     // build as innerHTML where available
5433     /** @ignore */
5434     var createHtml = function(o){
5435         if(typeof o == 'string'){
5436             return o;
5437         }
5438         var b = "";
5439         if(!o.tag){
5440             o.tag = "div";
5441         }
5442         b += "<" + o.tag;
5443         for(var attr in o){
5444             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5445             if(attr == "style"){
5446                 var s = o["style"];
5447                 if(typeof s == "function"){
5448                     s = s.call();
5449                 }
5450                 if(typeof s == "string"){
5451                     b += ' style="' + s + '"';
5452                 }else if(typeof s == "object"){
5453                     b += ' style="';
5454                     for(var key in s){
5455                         if(typeof s[key] != "function"){
5456                             b += key + ":" + s[key] + ";";
5457                         }
5458                     }
5459                     b += '"';
5460                 }
5461             }else{
5462                 if(attr == "cls"){
5463                     b += ' class="' + o["cls"] + '"';
5464                 }else if(attr == "htmlFor"){
5465                     b += ' for="' + o["htmlFor"] + '"';
5466                 }else{
5467                     b += " " + attr + '="' + o[attr] + '"';
5468                 }
5469             }
5470         }
5471         if(emptyTags.test(o.tag)){
5472             b += "/>";
5473         }else{
5474             b += ">";
5475             var cn = o.children || o.cn;
5476             if(cn){
5477                 //http://bugs.kde.org/show_bug.cgi?id=71506
5478                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5479                     for(var i = 0, len = cn.length; i < len; i++) {
5480                         b += createHtml(cn[i], b);
5481                     }
5482                 }else{
5483                     b += createHtml(cn, b);
5484                 }
5485             }
5486             if(o.html){
5487                 b += o.html;
5488             }
5489             b += "</" + o.tag + ">";
5490         }
5491         return b;
5492     };
5493
5494     // build as dom
5495     /** @ignore */
5496     var createDom = function(o, parentNode){
5497          
5498         // defininition craeted..
5499         var ns = false;
5500         if (o.ns && o.ns != 'html') {
5501                
5502             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5503                 xmlns[o.ns] = o.xmlns;
5504                 ns = o.xmlns;
5505             }
5506             if (typeof(xmlns[o.ns]) == 'undefined') {
5507                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5508             }
5509             ns = xmlns[o.ns];
5510         }
5511         
5512         
5513         if (typeof(o) == 'string') {
5514             return parentNode.appendChild(document.createTextNode(o));
5515         }
5516         o.tag = o.tag || div;
5517         if (o.ns && Roo.isIE) {
5518             ns = false;
5519             o.tag = o.ns + ':' + o.tag;
5520             
5521         }
5522         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5523         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5524         for(var attr in o){
5525             
5526             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5527                     attr == "style" || typeof o[attr] == "function") { continue; }
5528                     
5529             if(attr=="cls" && Roo.isIE){
5530                 el.className = o["cls"];
5531             }else{
5532                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5533                 else { 
5534                     el[attr] = o[attr];
5535                 }
5536             }
5537         }
5538         Roo.DomHelper.applyStyles(el, o.style);
5539         var cn = o.children || o.cn;
5540         if(cn){
5541             //http://bugs.kde.org/show_bug.cgi?id=71506
5542              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5543                 for(var i = 0, len = cn.length; i < len; i++) {
5544                     createDom(cn[i], el);
5545                 }
5546             }else{
5547                 createDom(cn, el);
5548             }
5549         }
5550         if(o.html){
5551             el.innerHTML = o.html;
5552         }
5553         if(parentNode){
5554            parentNode.appendChild(el);
5555         }
5556         return el;
5557     };
5558
5559     var ieTable = function(depth, s, h, e){
5560         tempTableEl.innerHTML = [s, h, e].join('');
5561         var i = -1, el = tempTableEl;
5562         while(++i < depth && el.firstChild){
5563             el = el.firstChild;
5564         }
5565         return el;
5566     };
5567
5568     // kill repeat to save bytes
5569     var ts = '<table>',
5570         te = '</table>',
5571         tbs = ts+'<tbody>',
5572         tbe = '</tbody>'+te,
5573         trs = tbs + '<tr>',
5574         tre = '</tr>'+tbe;
5575
5576     /**
5577      * @ignore
5578      * Nasty code for IE's broken table implementation
5579      */
5580     var insertIntoTable = function(tag, where, el, html){
5581         if(!tempTableEl){
5582             tempTableEl = document.createElement('div');
5583         }
5584         var node;
5585         var before = null;
5586         if(tag == 'td'){
5587             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5588                 return;
5589             }
5590             if(where == 'beforebegin'){
5591                 before = el;
5592                 el = el.parentNode;
5593             } else{
5594                 before = el.nextSibling;
5595                 el = el.parentNode;
5596             }
5597             node = ieTable(4, trs, html, tre);
5598         }
5599         else if(tag == 'tr'){
5600             if(where == 'beforebegin'){
5601                 before = el;
5602                 el = el.parentNode;
5603                 node = ieTable(3, tbs, html, tbe);
5604             } else if(where == 'afterend'){
5605                 before = el.nextSibling;
5606                 el = el.parentNode;
5607                 node = ieTable(3, tbs, html, tbe);
5608             } else{ // INTO a TR
5609                 if(where == 'afterbegin'){
5610                     before = el.firstChild;
5611                 }
5612                 node = ieTable(4, trs, html, tre);
5613             }
5614         } else if(tag == 'tbody'){
5615             if(where == 'beforebegin'){
5616                 before = el;
5617                 el = el.parentNode;
5618                 node = ieTable(2, ts, html, te);
5619             } else if(where == 'afterend'){
5620                 before = el.nextSibling;
5621                 el = el.parentNode;
5622                 node = ieTable(2, ts, html, te);
5623             } else{
5624                 if(where == 'afterbegin'){
5625                     before = el.firstChild;
5626                 }
5627                 node = ieTable(3, tbs, html, tbe);
5628             }
5629         } else{ // TABLE
5630             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5631                 return;
5632             }
5633             if(where == 'afterbegin'){
5634                 before = el.firstChild;
5635             }
5636             node = ieTable(2, ts, html, te);
5637         }
5638         el.insertBefore(node, before);
5639         return node;
5640     };
5641     
5642     // this is a bit like the react update code...
5643     // 
5644     
5645     var updateNode = function(from, to)
5646     {
5647         // should we handle non-standard elements?
5648         Roo.log(["UpdateNode" , from, to]);
5649         if (from.nodeType != to.nodeType) {
5650             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5651             from.parentNode.replaceChild(to, from);
5652         }
5653         
5654         if (from.nodeType == 3) {
5655             // assume it's text?!
5656             if (from.data == to.data) {
5657                 return;
5658             }
5659             from.data = to.data;
5660             return;
5661         }
5662         if (!from.parentNode) {
5663             // not sure why this is happening?
5664             return;
5665         }
5666         // assume 'to' doesnt have '1/3 nodetypes!
5667         // not sure why, by from, parent node might not exist?
5668         if (from.nodeType !=1 || from.tagName != to.tagName) {
5669             Roo.log(["ReplaceChild" , from, to ]);
5670             
5671             from.parentNode.replaceChild(to, from);
5672             return;
5673         }
5674         // compare attributes
5675         var ar = Array.from(from.attributes);
5676         for(var i = 0; i< ar.length;i++) {
5677             if (to.hasAttribute(ar[i].name)) {
5678                 continue;
5679             }
5680             if (ar[i].name == 'id') { // always keep ids?
5681                continue;
5682             }
5683             //if (ar[i].name == 'style') {
5684             //   throw "style removed?";
5685             //}
5686             Roo.log("removeAttribute" + ar[i].name);
5687             from.removeAttribute(ar[i].name);
5688         }
5689         ar = to.attributes;
5690         for(var i = 0; i< ar.length;i++) {
5691             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5692                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5693                 continue;
5694             }
5695             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5696             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5697         }
5698         // children
5699         var far = Array.from(from.childNodes);
5700         var tar = Array.from(to.childNodes);
5701         // if the lengths are different.. then it's probably a editable content change, rather than
5702         // a change of the block definition..
5703         
5704         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5705          /*if (from.innerHTML == to.innerHTML) {
5706             return;
5707         }
5708         if (far.length != tar.length) {
5709             from.innerHTML = to.innerHTML;
5710             return;
5711         }
5712         */
5713         
5714         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5715             if (i >= far.length) {
5716                 from.appendChild(tar[i]);
5717                 Roo.log(["add", tar[i]]);
5718                 
5719             } else if ( i  >= tar.length) {
5720                 from.removeChild(far[i]);
5721                 Roo.log(["remove", far[i]]);
5722             } else {
5723                 
5724                 updateNode(far[i], tar[i]);
5725             }    
5726         }
5727         
5728         
5729         
5730         
5731     };
5732     
5733     
5734
5735     return {
5736         /** True to force the use of DOM instead of html fragments @type Boolean */
5737         useDom : false,
5738     
5739         /**
5740          * Returns the markup for the passed Element(s) config
5741          * @param {Object} o The Dom object spec (and children)
5742          * @return {String}
5743          */
5744         markup : function(o){
5745             return createHtml(o);
5746         },
5747     
5748         /**
5749          * Applies a style specification to an element
5750          * @param {String/HTMLElement} el The element to apply styles to
5751          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5752          * a function which returns such a specification.
5753          */
5754         applyStyles : function(el, styles){
5755             if(styles){
5756                el = Roo.fly(el);
5757                if(typeof styles == "string"){
5758                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5759                    var matches;
5760                    while ((matches = re.exec(styles)) != null){
5761                        el.setStyle(matches[1], matches[2]);
5762                    }
5763                }else if (typeof styles == "object"){
5764                    for (var style in styles){
5765                       el.setStyle(style, styles[style]);
5766                    }
5767                }else if (typeof styles == "function"){
5768                     Roo.DomHelper.applyStyles(el, styles.call());
5769                }
5770             }
5771         },
5772     
5773         /**
5774          * Inserts an HTML fragment into the Dom
5775          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5776          * @param {HTMLElement} el The context element
5777          * @param {String} html The HTML fragmenet
5778          * @return {HTMLElement} The new node
5779          */
5780         insertHtml : function(where, el, html){
5781             where = where.toLowerCase();
5782             if(el.insertAdjacentHTML){
5783                 if(tableRe.test(el.tagName)){
5784                     var rs;
5785                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5786                         return rs;
5787                     }
5788                 }
5789                 switch(where){
5790                     case "beforebegin":
5791                         el.insertAdjacentHTML('BeforeBegin', html);
5792                         return el.previousSibling;
5793                     case "afterbegin":
5794                         el.insertAdjacentHTML('AfterBegin', html);
5795                         return el.firstChild;
5796                     case "beforeend":
5797                         el.insertAdjacentHTML('BeforeEnd', html);
5798                         return el.lastChild;
5799                     case "afterend":
5800                         el.insertAdjacentHTML('AfterEnd', html);
5801                         return el.nextSibling;
5802                 }
5803                 throw 'Illegal insertion point -> "' + where + '"';
5804             }
5805             var range = el.ownerDocument.createRange();
5806             var frag;
5807             switch(where){
5808                  case "beforebegin":
5809                     range.setStartBefore(el);
5810                     frag = range.createContextualFragment(html);
5811                     el.parentNode.insertBefore(frag, el);
5812                     return el.previousSibling;
5813                  case "afterbegin":
5814                     if(el.firstChild){
5815                         range.setStartBefore(el.firstChild);
5816                         frag = range.createContextualFragment(html);
5817                         el.insertBefore(frag, el.firstChild);
5818                         return el.firstChild;
5819                     }else{
5820                         el.innerHTML = html;
5821                         return el.firstChild;
5822                     }
5823                 case "beforeend":
5824                     if(el.lastChild){
5825                         range.setStartAfter(el.lastChild);
5826                         frag = range.createContextualFragment(html);
5827                         el.appendChild(frag);
5828                         return el.lastChild;
5829                     }else{
5830                         el.innerHTML = html;
5831                         return el.lastChild;
5832                     }
5833                 case "afterend":
5834                     range.setStartAfter(el);
5835                     frag = range.createContextualFragment(html);
5836                     el.parentNode.insertBefore(frag, el.nextSibling);
5837                     return el.nextSibling;
5838                 }
5839                 throw 'Illegal insertion point -> "' + where + '"';
5840         },
5841     
5842         /**
5843          * Creates new Dom element(s) and inserts them before el
5844          * @param {String/HTMLElement/Element} el The context element
5845          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5846          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5847          * @return {HTMLElement/Roo.Element} The new node
5848          */
5849         insertBefore : function(el, o, returnElement){
5850             return this.doInsert(el, o, returnElement, "beforeBegin");
5851         },
5852     
5853         /**
5854          * Creates new Dom element(s) and inserts them after el
5855          * @param {String/HTMLElement/Element} el The context element
5856          * @param {Object} o The Dom object spec (and children)
5857          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5858          * @return {HTMLElement/Roo.Element} The new node
5859          */
5860         insertAfter : function(el, o, returnElement){
5861             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5862         },
5863     
5864         /**
5865          * Creates new Dom element(s) and inserts them as the first child of el
5866          * @param {String/HTMLElement/Element} el The context element
5867          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5868          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5869          * @return {HTMLElement/Roo.Element} The new node
5870          */
5871         insertFirst : function(el, o, returnElement){
5872             return this.doInsert(el, o, returnElement, "afterBegin");
5873         },
5874     
5875         // private
5876         doInsert : function(el, o, returnElement, pos, sibling){
5877             el = Roo.getDom(el);
5878             var newNode;
5879             if(this.useDom || o.ns){
5880                 newNode = createDom(o, null);
5881                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5882             }else{
5883                 var html = createHtml(o);
5884                 newNode = this.insertHtml(pos, el, html);
5885             }
5886             return returnElement ? Roo.get(newNode, true) : newNode;
5887         },
5888     
5889         /**
5890          * Creates new Dom element(s) and appends them to el
5891          * @param {String/HTMLElement/Element} el The context element
5892          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5893          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5894          * @return {HTMLElement/Roo.Element} The new node
5895          */
5896         append : function(el, o, returnElement){
5897             el = Roo.getDom(el);
5898             var newNode;
5899             if(this.useDom || o.ns){
5900                 newNode = createDom(o, null);
5901                 el.appendChild(newNode);
5902             }else{
5903                 var html = createHtml(o);
5904                 newNode = this.insertHtml("beforeEnd", el, html);
5905             }
5906             return returnElement ? Roo.get(newNode, true) : newNode;
5907         },
5908     
5909         /**
5910          * Creates new Dom element(s) and overwrites the contents of el with them
5911          * @param {String/HTMLElement/Element} el The context element
5912          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5913          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5914          * @return {HTMLElement/Roo.Element} The new node
5915          */
5916         overwrite : function(el, o, returnElement)
5917         {
5918             el = Roo.getDom(el);
5919             if (o.ns) {
5920               
5921                 while (el.childNodes.length) {
5922                     el.removeChild(el.firstChild);
5923                 }
5924                 createDom(o, el);
5925             } else {
5926                 el.innerHTML = createHtml(o);   
5927             }
5928             
5929             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5930         },
5931     
5932         /**
5933          * Creates a new Roo.DomHelper.Template from the Dom object spec
5934          * @param {Object} o The Dom object spec (and children)
5935          * @return {Roo.DomHelper.Template} The new template
5936          */
5937         createTemplate : function(o){
5938             var html = createHtml(o);
5939             return new Roo.Template(html);
5940         },
5941          /**
5942          * Updates the first element with the spec from the o (replacing if necessary)
5943          * This iterates through the children, and updates attributes / children etc..
5944          * @param {String/HTMLElement/Element} el The context element
5945          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5946          */
5947         
5948         update : function(el, o)
5949         {
5950             updateNode(Roo.getDom(el), createDom(o));
5951             
5952         }
5953         
5954         
5955     };
5956 }();
5957 /*
5958  * Based on:
5959  * Ext JS Library 1.1.1
5960  * Copyright(c) 2006-2007, Ext JS, LLC.
5961  *
5962  * Originally Released Under LGPL - original licence link has changed is not relivant.
5963  *
5964  * Fork - LGPL
5965  * <script type="text/javascript">
5966  */
5967  
5968 /**
5969 * @class Roo.Template
5970 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5971 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5972 * Usage:
5973 <pre><code>
5974 var t = new Roo.Template({
5975     html :  '&lt;div name="{id}"&gt;' + 
5976         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5977         '&lt;/div&gt;',
5978     myformat: function (value, allValues) {
5979         return 'XX' + value;
5980     }
5981 });
5982 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5983 </code></pre>
5984 * For more information see this blog post with examples:
5985 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5986      - Create Elements using DOM, HTML fragments and Templates</a>. 
5987 * @constructor
5988 * @param {Object} cfg - Configuration object.
5989 */
5990 Roo.Template = function(cfg){
5991     // BC!
5992     if(cfg instanceof Array){
5993         cfg = cfg.join("");
5994     }else if(arguments.length > 1){
5995         cfg = Array.prototype.join.call(arguments, "");
5996     }
5997     
5998     
5999     if (typeof(cfg) == 'object') {
6000         Roo.apply(this,cfg)
6001     } else {
6002         // bc
6003         this.html = cfg;
6004     }
6005     if (this.url) {
6006         this.load();
6007     }
6008     
6009 };
6010 Roo.Template.prototype = {
6011     
6012     /**
6013      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6014      */
6015     onLoad : false,
6016     
6017     
6018     /**
6019      * @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..
6020      *                    it should be fixed so that template is observable...
6021      */
6022     url : false,
6023     /**
6024      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6025      */
6026     html : '',
6027     
6028     
6029     compiled : false,
6030     loaded : false,
6031     /**
6032      * Returns an HTML fragment of this template with the specified values applied.
6033      * @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'})
6034      * @return {String} The HTML fragment
6035      */
6036     
6037    
6038     
6039     applyTemplate : function(values){
6040         //Roo.log(["applyTemplate", values]);
6041         try {
6042            
6043             if(this.compiled){
6044                 return this.compiled(values);
6045             }
6046             var useF = this.disableFormats !== true;
6047             var fm = Roo.util.Format, tpl = this;
6048             var fn = function(m, name, format, args){
6049                 if(format && useF){
6050                     if(format.substr(0, 5) == "this."){
6051                         return tpl.call(format.substr(5), values[name], values);
6052                     }else{
6053                         if(args){
6054                             // quoted values are required for strings in compiled templates, 
6055                             // but for non compiled we need to strip them
6056                             // quoted reversed for jsmin
6057                             var re = /^\s*['"](.*)["']\s*$/;
6058                             args = args.split(',');
6059                             for(var i = 0, len = args.length; i < len; i++){
6060                                 args[i] = args[i].replace(re, "$1");
6061                             }
6062                             args = [values[name]].concat(args);
6063                         }else{
6064                             args = [values[name]];
6065                         }
6066                         return fm[format].apply(fm, args);
6067                     }
6068                 }else{
6069                     return values[name] !== undefined ? values[name] : "";
6070                 }
6071             };
6072             return this.html.replace(this.re, fn);
6073         } catch (e) {
6074             Roo.log(e);
6075             throw e;
6076         }
6077          
6078     },
6079     
6080     loading : false,
6081       
6082     load : function ()
6083     {
6084          
6085         if (this.loading) {
6086             return;
6087         }
6088         var _t = this;
6089         
6090         this.loading = true;
6091         this.compiled = false;
6092         
6093         var cx = new Roo.data.Connection();
6094         cx.request({
6095             url : this.url,
6096             method : 'GET',
6097             success : function (response) {
6098                 _t.loading = false;
6099                 _t.url = false;
6100                 
6101                 _t.set(response.responseText,true);
6102                 _t.loaded = true;
6103                 if (_t.onLoad) {
6104                     _t.onLoad();
6105                 }
6106              },
6107             failure : function(response) {
6108                 Roo.log("Template failed to load from " + _t.url);
6109                 _t.loading = false;
6110             }
6111         });
6112     },
6113
6114     /**
6115      * Sets the HTML used as the template and optionally compiles it.
6116      * @param {String} html
6117      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6118      * @return {Roo.Template} this
6119      */
6120     set : function(html, compile){
6121         this.html = html;
6122         this.compiled = false;
6123         if(compile){
6124             this.compile();
6125         }
6126         return this;
6127     },
6128     
6129     /**
6130      * True to disable format functions (defaults to false)
6131      * @type Boolean
6132      */
6133     disableFormats : false,
6134     
6135     /**
6136     * The regular expression used to match template variables 
6137     * @type RegExp
6138     * @property 
6139     */
6140     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6141     
6142     /**
6143      * Compiles the template into an internal function, eliminating the RegEx overhead.
6144      * @return {Roo.Template} this
6145      */
6146     compile : function(){
6147         var fm = Roo.util.Format;
6148         var useF = this.disableFormats !== true;
6149         var sep = Roo.isGecko ? "+" : ",";
6150         var fn = function(m, name, format, args){
6151             if(format && useF){
6152                 args = args ? ',' + args : "";
6153                 if(format.substr(0, 5) != "this."){
6154                     format = "fm." + format + '(';
6155                 }else{
6156                     format = 'this.call("'+ format.substr(5) + '", ';
6157                     args = ", values";
6158                 }
6159             }else{
6160                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6161             }
6162             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6163         };
6164         var body;
6165         // branched to use + in gecko and [].join() in others
6166         if(Roo.isGecko){
6167             body = "this.compiled = function(values){ return '" +
6168                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6169                     "';};";
6170         }else{
6171             body = ["this.compiled = function(values){ return ['"];
6172             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6173             body.push("'].join('');};");
6174             body = body.join('');
6175         }
6176         /**
6177          * eval:var:values
6178          * eval:var:fm
6179          */
6180         eval(body);
6181         return this;
6182     },
6183     
6184     // private function used to call members
6185     call : function(fnName, value, allValues){
6186         return this[fnName](value, allValues);
6187     },
6188     
6189     /**
6190      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6191      * @param {String/HTMLElement/Roo.Element} el The context element
6192      * @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'})
6193      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6194      * @return {HTMLElement/Roo.Element} The new node or Element
6195      */
6196     insertFirst: function(el, values, returnElement){
6197         return this.doInsert('afterBegin', el, values, returnElement);
6198     },
6199
6200     /**
6201      * Applies the supplied values to the template and inserts the new node(s) before el.
6202      * @param {String/HTMLElement/Roo.Element} el The context element
6203      * @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'})
6204      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6205      * @return {HTMLElement/Roo.Element} The new node or Element
6206      */
6207     insertBefore: function(el, values, returnElement){
6208         return this.doInsert('beforeBegin', el, values, returnElement);
6209     },
6210
6211     /**
6212      * Applies the supplied values to the template and inserts the new node(s) after el.
6213      * @param {String/HTMLElement/Roo.Element} el The context element
6214      * @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'})
6215      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6216      * @return {HTMLElement/Roo.Element} The new node or Element
6217      */
6218     insertAfter : function(el, values, returnElement){
6219         return this.doInsert('afterEnd', el, values, returnElement);
6220     },
6221     
6222     /**
6223      * Applies the supplied values to the template and appends the new node(s) to el.
6224      * @param {String/HTMLElement/Roo.Element} el The context element
6225      * @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'})
6226      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6227      * @return {HTMLElement/Roo.Element} The new node or Element
6228      */
6229     append : function(el, values, returnElement){
6230         return this.doInsert('beforeEnd', el, values, returnElement);
6231     },
6232
6233     doInsert : function(where, el, values, returnEl){
6234         el = Roo.getDom(el);
6235         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6236         return returnEl ? Roo.get(newNode, true) : newNode;
6237     },
6238
6239     /**
6240      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6241      * @param {String/HTMLElement/Roo.Element} el The context element
6242      * @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'})
6243      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6244      * @return {HTMLElement/Roo.Element} The new node or Element
6245      */
6246     overwrite : function(el, values, returnElement){
6247         el = Roo.getDom(el);
6248         el.innerHTML = this.applyTemplate(values);
6249         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6250     }
6251 };
6252 /**
6253  * Alias for {@link #applyTemplate}
6254  * @method
6255  */
6256 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6257
6258 // backwards compat
6259 Roo.DomHelper.Template = Roo.Template;
6260
6261 /**
6262  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6263  * @param {String/HTMLElement} el A DOM element or its id
6264  * @returns {Roo.Template} The created template
6265  * @static
6266  */
6267 Roo.Template.from = function(el){
6268     el = Roo.getDom(el);
6269     return new Roo.Template(el.value || el.innerHTML);
6270 };/*
6271  * Based on:
6272  * Ext JS Library 1.1.1
6273  * Copyright(c) 2006-2007, Ext JS, LLC.
6274  *
6275  * Originally Released Under LGPL - original licence link has changed is not relivant.
6276  *
6277  * Fork - LGPL
6278  * <script type="text/javascript">
6279  */
6280  
6281
6282 /*
6283  * This is code is also distributed under MIT license for use
6284  * with jQuery and prototype JavaScript libraries.
6285  */
6286 /**
6287  * @class Roo.DomQuery
6288 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).
6289 <p>
6290 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>
6291
6292 <p>
6293 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.
6294 </p>
6295 <h4>Element Selectors:</h4>
6296 <ul class="list">
6297     <li> <b>*</b> any element</li>
6298     <li> <b>E</b> an element with the tag E</li>
6299     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6300     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6301     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6302     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6303 </ul>
6304 <h4>Attribute Selectors:</h4>
6305 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6306 <ul class="list">
6307     <li> <b>E[foo]</b> has an attribute "foo"</li>
6308     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6309     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6310     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6311     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6312     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6313     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6314 </ul>
6315 <h4>Pseudo Classes:</h4>
6316 <ul class="list">
6317     <li> <b>E:first-child</b> E is the first child of its parent</li>
6318     <li> <b>E:last-child</b> E is the last child of its parent</li>
6319     <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>
6320     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6321     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6322     <li> <b>E:only-child</b> E is the only child of its parent</li>
6323     <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>
6324     <li> <b>E:first</b> the first E in the resultset</li>
6325     <li> <b>E:last</b> the last E in the resultset</li>
6326     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6327     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6328     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6329     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6330     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6331     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6332     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6333     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6334     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6335 </ul>
6336 <h4>CSS Value Selectors:</h4>
6337 <ul class="list">
6338     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6339     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6340     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6341     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6342     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6343     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6344 </ul>
6345  * @static
6346  */
6347 Roo.DomQuery = function(){
6348     var cache = {}, simpleCache = {}, valueCache = {};
6349     var nonSpace = /\S/;
6350     var trimRe = /^\s+|\s+$/g;
6351     var tplRe = /\{(\d+)\}/g;
6352     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6353     var tagTokenRe = /^(#)?([\w-\*]+)/;
6354     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6355
6356     function child(p, index){
6357         var i = 0;
6358         var n = p.firstChild;
6359         while(n){
6360             if(n.nodeType == 1){
6361                if(++i == index){
6362                    return n;
6363                }
6364             }
6365             n = n.nextSibling;
6366         }
6367         return null;
6368     };
6369
6370     function next(n){
6371         while((n = n.nextSibling) && n.nodeType != 1);
6372         return n;
6373     };
6374
6375     function prev(n){
6376         while((n = n.previousSibling) && n.nodeType != 1);
6377         return n;
6378     };
6379
6380     function children(d){
6381         var n = d.firstChild, ni = -1;
6382             while(n){
6383                 var nx = n.nextSibling;
6384                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6385                     d.removeChild(n);
6386                 }else{
6387                     n.nodeIndex = ++ni;
6388                 }
6389                 n = nx;
6390             }
6391             return this;
6392         };
6393
6394     function byClassName(c, a, v){
6395         if(!v){
6396             return c;
6397         }
6398         var r = [], ri = -1, cn;
6399         for(var i = 0, ci; ci = c[i]; i++){
6400             
6401             
6402             if((' '+
6403                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6404                  +' ').indexOf(v) != -1){
6405                 r[++ri] = ci;
6406             }
6407         }
6408         return r;
6409     };
6410
6411     function attrValue(n, attr){
6412         if(!n.tagName && typeof n.length != "undefined"){
6413             n = n[0];
6414         }
6415         if(!n){
6416             return null;
6417         }
6418         if(attr == "for"){
6419             return n.htmlFor;
6420         }
6421         if(attr == "class" || attr == "className"){
6422             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6423         }
6424         return n.getAttribute(attr) || n[attr];
6425
6426     };
6427
6428     function getNodes(ns, mode, tagName){
6429         var result = [], ri = -1, cs;
6430         if(!ns){
6431             return result;
6432         }
6433         tagName = tagName || "*";
6434         if(typeof ns.getElementsByTagName != "undefined"){
6435             ns = [ns];
6436         }
6437         if(!mode){
6438             for(var i = 0, ni; ni = ns[i]; i++){
6439                 cs = ni.getElementsByTagName(tagName);
6440                 for(var j = 0, ci; ci = cs[j]; j++){
6441                     result[++ri] = ci;
6442                 }
6443             }
6444         }else if(mode == "/" || mode == ">"){
6445             var utag = tagName.toUpperCase();
6446             for(var i = 0, ni, cn; ni = ns[i]; i++){
6447                 cn = ni.children || ni.childNodes;
6448                 for(var j = 0, cj; cj = cn[j]; j++){
6449                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6450                         result[++ri] = cj;
6451                     }
6452                 }
6453             }
6454         }else if(mode == "+"){
6455             var utag = tagName.toUpperCase();
6456             for(var i = 0, n; n = ns[i]; i++){
6457                 while((n = n.nextSibling) && n.nodeType != 1);
6458                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6459                     result[++ri] = n;
6460                 }
6461             }
6462         }else if(mode == "~"){
6463             for(var i = 0, n; n = ns[i]; i++){
6464                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6465                 if(n){
6466                     result[++ri] = n;
6467                 }
6468             }
6469         }
6470         return result;
6471     };
6472
6473     function concat(a, b){
6474         if(b.slice){
6475             return a.concat(b);
6476         }
6477         for(var i = 0, l = b.length; i < l; i++){
6478             a[a.length] = b[i];
6479         }
6480         return a;
6481     }
6482
6483     function byTag(cs, tagName){
6484         if(cs.tagName || cs == document){
6485             cs = [cs];
6486         }
6487         if(!tagName){
6488             return cs;
6489         }
6490         var r = [], ri = -1;
6491         tagName = tagName.toLowerCase();
6492         for(var i = 0, ci; ci = cs[i]; i++){
6493             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6494                 r[++ri] = ci;
6495             }
6496         }
6497         return r;
6498     };
6499
6500     function byId(cs, attr, id){
6501         if(cs.tagName || cs == document){
6502             cs = [cs];
6503         }
6504         if(!id){
6505             return cs;
6506         }
6507         var r = [], ri = -1;
6508         for(var i = 0,ci; ci = cs[i]; i++){
6509             if(ci && ci.id == id){
6510                 r[++ri] = ci;
6511                 return r;
6512             }
6513         }
6514         return r;
6515     };
6516
6517     function byAttribute(cs, attr, value, op, custom){
6518         var r = [], ri = -1, st = custom=="{";
6519         var f = Roo.DomQuery.operators[op];
6520         for(var i = 0, ci; ci = cs[i]; i++){
6521             var a;
6522             if(st){
6523                 a = Roo.DomQuery.getStyle(ci, attr);
6524             }
6525             else if(attr == "class" || attr == "className"){
6526                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6527             }else if(attr == "for"){
6528                 a = ci.htmlFor;
6529             }else if(attr == "href"){
6530                 a = ci.getAttribute("href", 2);
6531             }else{
6532                 a = ci.getAttribute(attr);
6533             }
6534             if((f && f(a, value)) || (!f && a)){
6535                 r[++ri] = ci;
6536             }
6537         }
6538         return r;
6539     };
6540
6541     function byPseudo(cs, name, value){
6542         return Roo.DomQuery.pseudos[name](cs, value);
6543     };
6544
6545     // This is for IE MSXML which does not support expandos.
6546     // IE runs the same speed using setAttribute, however FF slows way down
6547     // and Safari completely fails so they need to continue to use expandos.
6548     var isIE = window.ActiveXObject ? true : false;
6549
6550     // this eval is stop the compressor from
6551     // renaming the variable to something shorter
6552     
6553     /** eval:var:batch */
6554     var batch = 30803; 
6555
6556     var key = 30803;
6557
6558     function nodupIEXml(cs){
6559         var d = ++key;
6560         cs[0].setAttribute("_nodup", d);
6561         var r = [cs[0]];
6562         for(var i = 1, len = cs.length; i < len; i++){
6563             var c = cs[i];
6564             if(!c.getAttribute("_nodup") != d){
6565                 c.setAttribute("_nodup", d);
6566                 r[r.length] = c;
6567             }
6568         }
6569         for(var i = 0, len = cs.length; i < len; i++){
6570             cs[i].removeAttribute("_nodup");
6571         }
6572         return r;
6573     }
6574
6575     function nodup(cs){
6576         if(!cs){
6577             return [];
6578         }
6579         var len = cs.length, c, i, r = cs, cj, ri = -1;
6580         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6581             return cs;
6582         }
6583         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6584             return nodupIEXml(cs);
6585         }
6586         var d = ++key;
6587         cs[0]._nodup = d;
6588         for(i = 1; c = cs[i]; i++){
6589             if(c._nodup != d){
6590                 c._nodup = d;
6591             }else{
6592                 r = [];
6593                 for(var j = 0; j < i; j++){
6594                     r[++ri] = cs[j];
6595                 }
6596                 for(j = i+1; cj = cs[j]; j++){
6597                     if(cj._nodup != d){
6598                         cj._nodup = d;
6599                         r[++ri] = cj;
6600                     }
6601                 }
6602                 return r;
6603             }
6604         }
6605         return r;
6606     }
6607
6608     function quickDiffIEXml(c1, c2){
6609         var d = ++key;
6610         for(var i = 0, len = c1.length; i < len; i++){
6611             c1[i].setAttribute("_qdiff", d);
6612         }
6613         var r = [];
6614         for(var i = 0, len = c2.length; i < len; i++){
6615             if(c2[i].getAttribute("_qdiff") != d){
6616                 r[r.length] = c2[i];
6617             }
6618         }
6619         for(var i = 0, len = c1.length; i < len; i++){
6620            c1[i].removeAttribute("_qdiff");
6621         }
6622         return r;
6623     }
6624
6625     function quickDiff(c1, c2){
6626         var len1 = c1.length;
6627         if(!len1){
6628             return c2;
6629         }
6630         if(isIE && c1[0].selectSingleNode){
6631             return quickDiffIEXml(c1, c2);
6632         }
6633         var d = ++key;
6634         for(var i = 0; i < len1; i++){
6635             c1[i]._qdiff = d;
6636         }
6637         var r = [];
6638         for(var i = 0, len = c2.length; i < len; i++){
6639             if(c2[i]._qdiff != d){
6640                 r[r.length] = c2[i];
6641             }
6642         }
6643         return r;
6644     }
6645
6646     function quickId(ns, mode, root, id){
6647         if(ns == root){
6648            var d = root.ownerDocument || root;
6649            return d.getElementById(id);
6650         }
6651         ns = getNodes(ns, mode, "*");
6652         return byId(ns, null, id);
6653     }
6654
6655     return {
6656         getStyle : function(el, name){
6657             return Roo.fly(el).getStyle(name);
6658         },
6659         /**
6660          * Compiles a selector/xpath query into a reusable function. The returned function
6661          * takes one parameter "root" (optional), which is the context node from where the query should start.
6662          * @param {String} selector The selector/xpath query
6663          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6664          * @return {Function}
6665          */
6666         compile : function(path, type){
6667             type = type || "select";
6668             
6669             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6670             var q = path, mode, lq;
6671             var tk = Roo.DomQuery.matchers;
6672             var tklen = tk.length;
6673             var mm;
6674
6675             // accept leading mode switch
6676             var lmode = q.match(modeRe);
6677             if(lmode && lmode[1]){
6678                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6679                 q = q.replace(lmode[1], "");
6680             }
6681             // strip leading slashes
6682             while(path.substr(0, 1)=="/"){
6683                 path = path.substr(1);
6684             }
6685
6686             while(q && lq != q){
6687                 lq = q;
6688                 var tm = q.match(tagTokenRe);
6689                 if(type == "select"){
6690                     if(tm){
6691                         if(tm[1] == "#"){
6692                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6693                         }else{
6694                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6695                         }
6696                         q = q.replace(tm[0], "");
6697                     }else if(q.substr(0, 1) != '@'){
6698                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6699                     }
6700                 }else{
6701                     if(tm){
6702                         if(tm[1] == "#"){
6703                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6704                         }else{
6705                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6706                         }
6707                         q = q.replace(tm[0], "");
6708                     }
6709                 }
6710                 while(!(mm = q.match(modeRe))){
6711                     var matched = false;
6712                     for(var j = 0; j < tklen; j++){
6713                         var t = tk[j];
6714                         var m = q.match(t.re);
6715                         if(m){
6716                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6717                                                     return m[i];
6718                                                 });
6719                             q = q.replace(m[0], "");
6720                             matched = true;
6721                             break;
6722                         }
6723                     }
6724                     // prevent infinite loop on bad selector
6725                     if(!matched){
6726                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6727                     }
6728                 }
6729                 if(mm[1]){
6730                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6731                     q = q.replace(mm[1], "");
6732                 }
6733             }
6734             fn[fn.length] = "return nodup(n);\n}";
6735             
6736              /** 
6737               * list of variables that need from compression as they are used by eval.
6738              *  eval:var:batch 
6739              *  eval:var:nodup
6740              *  eval:var:byTag
6741              *  eval:var:ById
6742              *  eval:var:getNodes
6743              *  eval:var:quickId
6744              *  eval:var:mode
6745              *  eval:var:root
6746              *  eval:var:n
6747              *  eval:var:byClassName
6748              *  eval:var:byPseudo
6749              *  eval:var:byAttribute
6750              *  eval:var:attrValue
6751              * 
6752              **/ 
6753             eval(fn.join(""));
6754             return f;
6755         },
6756
6757         /**
6758          * Selects a group of elements.
6759          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6760          * @param {Node} root (optional) The start of the query (defaults to document).
6761          * @return {Array}
6762          */
6763         select : function(path, root, type){
6764             if(!root || root == document){
6765                 root = document;
6766             }
6767             if(typeof root == "string"){
6768                 root = document.getElementById(root);
6769             }
6770             var paths = path.split(",");
6771             var results = [];
6772             for(var i = 0, len = paths.length; i < len; i++){
6773                 var p = paths[i].replace(trimRe, "");
6774                 if(!cache[p]){
6775                     cache[p] = Roo.DomQuery.compile(p);
6776                     if(!cache[p]){
6777                         throw p + " is not a valid selector";
6778                     }
6779                 }
6780                 var result = cache[p](root);
6781                 if(result && result != document){
6782                     results = results.concat(result);
6783                 }
6784             }
6785             if(paths.length > 1){
6786                 return nodup(results);
6787             }
6788             return results;
6789         },
6790
6791         /**
6792          * Selects a single element.
6793          * @param {String} selector The selector/xpath query
6794          * @param {Node} root (optional) The start of the query (defaults to document).
6795          * @return {Element}
6796          */
6797         selectNode : function(path, root){
6798             return Roo.DomQuery.select(path, root)[0];
6799         },
6800
6801         /**
6802          * Selects the value of a node, optionally replacing null with the defaultValue.
6803          * @param {String} selector The selector/xpath query
6804          * @param {Node} root (optional) The start of the query (defaults to document).
6805          * @param {String} defaultValue
6806          */
6807         selectValue : function(path, root, defaultValue){
6808             path = path.replace(trimRe, "");
6809             if(!valueCache[path]){
6810                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6811             }
6812             var n = valueCache[path](root);
6813             n = n[0] ? n[0] : n;
6814             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6815             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6816         },
6817
6818         /**
6819          * Selects the value of a node, parsing integers and floats.
6820          * @param {String} selector The selector/xpath query
6821          * @param {Node} root (optional) The start of the query (defaults to document).
6822          * @param {Number} defaultValue
6823          * @return {Number}
6824          */
6825         selectNumber : function(path, root, defaultValue){
6826             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6827             return parseFloat(v);
6828         },
6829
6830         /**
6831          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6832          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6833          * @param {String} selector The simple selector to test
6834          * @return {Boolean}
6835          */
6836         is : function(el, ss){
6837             if(typeof el == "string"){
6838                 el = document.getElementById(el);
6839             }
6840             var isArray = (el instanceof Array);
6841             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6842             return isArray ? (result.length == el.length) : (result.length > 0);
6843         },
6844
6845         /**
6846          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6847          * @param {Array} el An array of elements to filter
6848          * @param {String} selector The simple selector to test
6849          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6850          * the selector instead of the ones that match
6851          * @return {Array}
6852          */
6853         filter : function(els, ss, nonMatches){
6854             ss = ss.replace(trimRe, "");
6855             if(!simpleCache[ss]){
6856                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6857             }
6858             var result = simpleCache[ss](els);
6859             return nonMatches ? quickDiff(result, els) : result;
6860         },
6861
6862         /**
6863          * Collection of matching regular expressions and code snippets.
6864          */
6865         matchers : [{
6866                 re: /^\.([\w-]+)/,
6867                 select: 'n = byClassName(n, null, " {1} ");'
6868             }, {
6869                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6870                 select: 'n = byPseudo(n, "{1}", "{2}");'
6871             },{
6872                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6873                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6874             }, {
6875                 re: /^#([\w-]+)/,
6876                 select: 'n = byId(n, null, "{1}");'
6877             },{
6878                 re: /^@([\w-]+)/,
6879                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6880             }
6881         ],
6882
6883         /**
6884          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6885          * 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;.
6886          */
6887         operators : {
6888             "=" : function(a, v){
6889                 return a == v;
6890             },
6891             "!=" : function(a, v){
6892                 return a != v;
6893             },
6894             "^=" : function(a, v){
6895                 return a && a.substr(0, v.length) == v;
6896             },
6897             "$=" : function(a, v){
6898                 return a && a.substr(a.length-v.length) == v;
6899             },
6900             "*=" : function(a, v){
6901                 return a && a.indexOf(v) !== -1;
6902             },
6903             "%=" : function(a, v){
6904                 return (a % v) == 0;
6905             },
6906             "|=" : function(a, v){
6907                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6908             },
6909             "~=" : function(a, v){
6910                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6911             }
6912         },
6913
6914         /**
6915          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6916          * and the argument (if any) supplied in the selector.
6917          */
6918         pseudos : {
6919             "first-child" : function(c){
6920                 var r = [], ri = -1, n;
6921                 for(var i = 0, ci; ci = n = c[i]; i++){
6922                     while((n = n.previousSibling) && n.nodeType != 1);
6923                     if(!n){
6924                         r[++ri] = ci;
6925                     }
6926                 }
6927                 return r;
6928             },
6929
6930             "last-child" : function(c){
6931                 var r = [], ri = -1, n;
6932                 for(var i = 0, ci; ci = n = c[i]; i++){
6933                     while((n = n.nextSibling) && n.nodeType != 1);
6934                     if(!n){
6935                         r[++ri] = ci;
6936                     }
6937                 }
6938                 return r;
6939             },
6940
6941             "nth-child" : function(c, a) {
6942                 var r = [], ri = -1;
6943                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6944                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6945                 for(var i = 0, n; n = c[i]; i++){
6946                     var pn = n.parentNode;
6947                     if (batch != pn._batch) {
6948                         var j = 0;
6949                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6950                             if(cn.nodeType == 1){
6951                                cn.nodeIndex = ++j;
6952                             }
6953                         }
6954                         pn._batch = batch;
6955                     }
6956                     if (f == 1) {
6957                         if (l == 0 || n.nodeIndex == l){
6958                             r[++ri] = n;
6959                         }
6960                     } else if ((n.nodeIndex + l) % f == 0){
6961                         r[++ri] = n;
6962                     }
6963                 }
6964
6965                 return r;
6966             },
6967
6968             "only-child" : function(c){
6969                 var r = [], ri = -1;;
6970                 for(var i = 0, ci; ci = c[i]; i++){
6971                     if(!prev(ci) && !next(ci)){
6972                         r[++ri] = ci;
6973                     }
6974                 }
6975                 return r;
6976             },
6977
6978             "empty" : function(c){
6979                 var r = [], ri = -1;
6980                 for(var i = 0, ci; ci = c[i]; i++){
6981                     var cns = ci.childNodes, j = 0, cn, empty = true;
6982                     while(cn = cns[j]){
6983                         ++j;
6984                         if(cn.nodeType == 1 || cn.nodeType == 3){
6985                             empty = false;
6986                             break;
6987                         }
6988                     }
6989                     if(empty){
6990                         r[++ri] = ci;
6991                     }
6992                 }
6993                 return r;
6994             },
6995
6996             "contains" : function(c, v){
6997                 var r = [], ri = -1;
6998                 for(var i = 0, ci; ci = c[i]; i++){
6999                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7000                         r[++ri] = ci;
7001                     }
7002                 }
7003                 return r;
7004             },
7005
7006             "nodeValue" : function(c, v){
7007                 var r = [], ri = -1;
7008                 for(var i = 0, ci; ci = c[i]; i++){
7009                     if(ci.firstChild && ci.firstChild.nodeValue == v){
7010                         r[++ri] = ci;
7011                     }
7012                 }
7013                 return r;
7014             },
7015
7016             "checked" : function(c){
7017                 var r = [], ri = -1;
7018                 for(var i = 0, ci; ci = c[i]; i++){
7019                     if(ci.checked == true){
7020                         r[++ri] = ci;
7021                     }
7022                 }
7023                 return r;
7024             },
7025
7026             "not" : function(c, ss){
7027                 return Roo.DomQuery.filter(c, ss, true);
7028             },
7029
7030             "odd" : function(c){
7031                 return this["nth-child"](c, "odd");
7032             },
7033
7034             "even" : function(c){
7035                 return this["nth-child"](c, "even");
7036             },
7037
7038             "nth" : function(c, a){
7039                 return c[a-1] || [];
7040             },
7041
7042             "first" : function(c){
7043                 return c[0] || [];
7044             },
7045
7046             "last" : function(c){
7047                 return c[c.length-1] || [];
7048             },
7049
7050             "has" : function(c, ss){
7051                 var s = Roo.DomQuery.select;
7052                 var r = [], ri = -1;
7053                 for(var i = 0, ci; ci = c[i]; i++){
7054                     if(s(ss, ci).length > 0){
7055                         r[++ri] = ci;
7056                     }
7057                 }
7058                 return r;
7059             },
7060
7061             "next" : function(c, ss){
7062                 var is = Roo.DomQuery.is;
7063                 var r = [], ri = -1;
7064                 for(var i = 0, ci; ci = c[i]; i++){
7065                     var n = next(ci);
7066                     if(n && is(n, ss)){
7067                         r[++ri] = ci;
7068                     }
7069                 }
7070                 return r;
7071             },
7072
7073             "prev" : function(c, ss){
7074                 var is = Roo.DomQuery.is;
7075                 var r = [], ri = -1;
7076                 for(var i = 0, ci; ci = c[i]; i++){
7077                     var n = prev(ci);
7078                     if(n && is(n, ss)){
7079                         r[++ri] = ci;
7080                     }
7081                 }
7082                 return r;
7083             }
7084         }
7085     };
7086 }();
7087
7088 /**
7089  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7090  * @param {String} path The selector/xpath query
7091  * @param {Node} root (optional) The start of the query (defaults to document).
7092  * @return {Array}
7093  * @member Roo
7094  * @method query
7095  */
7096 Roo.query = Roo.DomQuery.select;
7097 /*
7098  * Based on:
7099  * Ext JS Library 1.1.1
7100  * Copyright(c) 2006-2007, Ext JS, LLC.
7101  *
7102  * Originally Released Under LGPL - original licence link has changed is not relivant.
7103  *
7104  * Fork - LGPL
7105  * <script type="text/javascript">
7106  */
7107
7108 /**
7109  * @class Roo.util.Observable
7110  * Base class that provides a common interface for publishing events. Subclasses are expected to
7111  * to have a property "events" with all the events defined.<br>
7112  * For example:
7113  * <pre><code>
7114  Employee = function(name){
7115     this.name = name;
7116     this.addEvents({
7117         "fired" : true,
7118         "quit" : true
7119     });
7120  }
7121  Roo.extend(Employee, Roo.util.Observable);
7122 </code></pre>
7123  * @param {Object} config properties to use (incuding events / listeners)
7124  */
7125
7126 Roo.util.Observable = function(cfg){
7127     console.log("UTIL OBSERVABLE CONSTRUCTOR");
7128     
7129     cfg = cfg|| {};
7130     this.addEvents(cfg.events || {});
7131     if (cfg.events) {
7132         delete cfg.events; // make sure
7133     }
7134      
7135     Roo.apply(this, cfg);
7136     
7137     if(this.listeners){
7138         this.on(this.listeners);
7139         delete this.listeners;
7140     }
7141 };
7142 Roo.util.Observable.prototype = {
7143     /** 
7144  * @cfg {Object} listeners  list of events and functions to call for this object, 
7145  * For example :
7146  * <pre><code>
7147     listeners :  { 
7148        'click' : function(e) {
7149            ..... 
7150         } ,
7151         .... 
7152     } 
7153   </code></pre>
7154  */
7155     
7156     
7157     /**
7158      * Fires the specified event with the passed parameters (minus the event name).
7159      * @param {String} eventName
7160      * @param {Object...} args Variable number of parameters are passed to handlers
7161      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7162      */
7163     fireEvent : function(){
7164         var ce = this.events[arguments[0].toLowerCase()];
7165         if(typeof ce == "object"){
7166             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7167         }else{
7168             return true;
7169         }
7170     },
7171
7172     // private
7173     filterOptRe : /^(?:scope|delay|buffer|single)$/,
7174
7175     /**
7176      * Appends an event handler to this component
7177      * @param {String}   eventName The type of event to listen for
7178      * @param {Function} handler The method the event invokes
7179      * @param {Object}   scope (optional) The scope in which to execute the handler
7180      * function. The handler function's "this" context.
7181      * @param {Object}   options (optional) An object containing handler configuration
7182      * properties. This may contain any of the following properties:<ul>
7183      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7184      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7185      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7186      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7187      * by the specified number of milliseconds. If the event fires again within that time, the original
7188      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7189      * </ul><br>
7190      * <p>
7191      * <b>Combining Options</b><br>
7192      * Using the options argument, it is possible to combine different types of listeners:<br>
7193      * <br>
7194      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7195                 <pre><code>
7196                 el.on('click', this.onClick, this, {
7197                         single: true,
7198                 delay: 100,
7199                 forumId: 4
7200                 });
7201                 </code></pre>
7202      * <p>
7203      * <b>Attaching multiple handlers in 1 call</b><br>
7204      * The method also allows for a single argument to be passed which is a config object containing properties
7205      * which specify multiple handlers.
7206      * <pre><code>
7207                 el.on({
7208                         'click': {
7209                         fn: this.onClick,
7210                         scope: this,
7211                         delay: 100
7212                 }, 
7213                 'mouseover': {
7214                         fn: this.onMouseOver,
7215                         scope: this
7216                 },
7217                 'mouseout': {
7218                         fn: this.onMouseOut,
7219                         scope: this
7220                 }
7221                 });
7222                 </code></pre>
7223      * <p>
7224      * Or a shorthand syntax which passes the same scope object to all handlers:
7225         <pre><code>
7226                 el.on({
7227                         'click': this.onClick,
7228                 'mouseover': this.onMouseOver,
7229                 'mouseout': this.onMouseOut,
7230                 scope: this
7231                 });
7232                 </code></pre>
7233      */
7234     addListener : function(eventName, fn, scope, o){
7235         if(typeof eventName == "object"){
7236             o = eventName;
7237             for(var e in o){
7238                 if(this.filterOptRe.test(e)){
7239                     continue;
7240                 }
7241                 if(typeof o[e] == "function"){
7242                     // shared options
7243                     this.addListener(e, o[e], o.scope,  o);
7244                 }else{
7245                     // individual options
7246                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7247                 }
7248             }
7249             return;
7250         }
7251         o = (!o || typeof o == "boolean") ? {} : o;
7252         eventName = eventName.toLowerCase();
7253         var ce = this.events[eventName] || true;
7254         if(typeof ce == "boolean"){
7255             ce = new Roo.util.Event(this, eventName);
7256             this.events[eventName] = ce;
7257         }
7258         ce.addListener(fn, scope, o);
7259     },
7260
7261     /**
7262      * Removes a listener
7263      * @param {String}   eventName     The type of event to listen for
7264      * @param {Function} handler        The handler to remove
7265      * @param {Object}   scope  (optional) The scope (this object) for the handler
7266      */
7267     removeListener : function(eventName, fn, scope){
7268         var ce = this.events[eventName.toLowerCase()];
7269         if(typeof ce == "object"){
7270             ce.removeListener(fn, scope);
7271         }
7272     },
7273
7274     /**
7275      * Removes all listeners for this object
7276      */
7277     purgeListeners : function(){
7278         for(var evt in this.events){
7279             if(typeof this.events[evt] == "object"){
7280                  this.events[evt].clearListeners();
7281             }
7282         }
7283     },
7284
7285     relayEvents : function(o, events){
7286         var createHandler = function(ename){
7287             return function(){
7288                  
7289                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7290             };
7291         };
7292         for(var i = 0, len = events.length; i < len; i++){
7293             var ename = events[i];
7294             if(!this.events[ename]){
7295                 this.events[ename] = true;
7296             };
7297             o.on(ename, createHandler(ename), this);
7298         }
7299     },
7300
7301     /**
7302      * Used to define events on this Observable
7303      * @param {Object} object The object with the events defined
7304      */
7305     addEvents : function(o){
7306         if(!this.events){
7307             this.events = {};
7308         }
7309         Roo.applyIf(this.events, o);
7310     },
7311
7312     /**
7313      * Checks to see if this object has any listeners for a specified event
7314      * @param {String} eventName The name of the event to check for
7315      * @return {Boolean} True if the event is being listened for, else false
7316      */
7317     hasListener : function(eventName){
7318         var e = this.events[eventName];
7319         return typeof e == "object" && e.listeners.length > 0;
7320     }
7321 };
7322 /**
7323  * Appends an event handler to this element (shorthand for addListener)
7324  * @param {String}   eventName     The type of event to listen for
7325  * @param {Function} handler        The method the event invokes
7326  * @param {Object}   scope (optional) The scope in which to execute the handler
7327  * function. The handler function's "this" context.
7328  * @param {Object}   options  (optional)
7329  * @method
7330  */
7331 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7332 /**
7333  * Removes a listener (shorthand for removeListener)
7334  * @param {String}   eventName     The type of event to listen for
7335  * @param {Function} handler        The handler to remove
7336  * @param {Object}   scope  (optional) The scope (this object) for the handler
7337  * @method
7338  */
7339 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7340
7341 /**
7342  * Starts capture on the specified Observable. All events will be passed
7343  * to the supplied function with the event name + standard signature of the event
7344  * <b>before</b> the event is fired. If the supplied function returns false,
7345  * the event will not fire.
7346  * @param {Observable} o The Observable to capture
7347  * @param {Function} fn The function to call
7348  * @param {Object} scope (optional) The scope (this object) for the fn
7349  * @static
7350  */
7351 Roo.util.Observable.capture = function(o, fn, scope){
7352     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7353 };
7354
7355 /**
7356  * Removes <b>all</b> added captures from the Observable.
7357  * @param {Observable} o The Observable to release
7358  * @static
7359  */
7360 Roo.util.Observable.releaseCapture = function(o){
7361     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7362 };
7363
7364 (function(){
7365
7366     var createBuffered = function(h, o, scope){
7367         var task = new Roo.util.DelayedTask();
7368         return function(){
7369             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7370         };
7371     };
7372
7373     var createSingle = function(h, e, fn, scope){
7374         return function(){
7375             e.removeListener(fn, scope);
7376             return h.apply(scope, arguments);
7377         };
7378     };
7379
7380     var createDelayed = function(h, o, scope){
7381         return function(){
7382             var args = Array.prototype.slice.call(arguments, 0);
7383             setTimeout(function(){
7384                 h.apply(scope, args);
7385             }, o.delay || 10);
7386         };
7387     };
7388
7389     Roo.util.Event = function(obj, name){
7390         this.name = name;
7391         this.obj = obj;
7392         this.listeners = [];
7393     };
7394
7395     Roo.util.Event.prototype = {
7396         addListener : function(fn, scope, options){
7397             var o = options || {};
7398             scope = scope || this.obj;
7399             if(!this.isListening(fn, scope)){
7400                 var l = {fn: fn, scope: scope, options: o};
7401                 var h = fn;
7402                 if(o.delay){
7403                     h = createDelayed(h, o, scope);
7404                 }
7405                 if(o.single){
7406                     h = createSingle(h, this, fn, scope);
7407                 }
7408                 if(o.buffer){
7409                     h = createBuffered(h, o, scope);
7410                 }
7411                 l.fireFn = h;
7412                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7413                     this.listeners.push(l);
7414                 }else{
7415                     this.listeners = this.listeners.slice(0);
7416                     this.listeners.push(l);
7417                 }
7418             }
7419         },
7420
7421         findListener : function(fn, scope){
7422             scope = scope || this.obj;
7423             var ls = this.listeners;
7424             for(var i = 0, len = ls.length; i < len; i++){
7425                 var l = ls[i];
7426                 if(l.fn == fn && l.scope == scope){
7427                     return i;
7428                 }
7429             }
7430             return -1;
7431         },
7432
7433         isListening : function(fn, scope){
7434             return this.findListener(fn, scope) != -1;
7435         },
7436
7437         removeListener : function(fn, scope){
7438             var index;
7439             if((index = this.findListener(fn, scope)) != -1){
7440                 if(!this.firing){
7441                     this.listeners.splice(index, 1);
7442                 }else{
7443                     this.listeners = this.listeners.slice(0);
7444                     this.listeners.splice(index, 1);
7445                 }
7446                 return true;
7447             }
7448             return false;
7449         },
7450
7451         clearListeners : function(){
7452             this.listeners = [];
7453         },
7454
7455         fire : function(){
7456             var ls = this.listeners, scope, len = ls.length;
7457             if(len > 0){
7458                 this.firing = true;
7459                 var args = Array.prototype.slice.call(arguments, 0);                
7460                 for(var i = 0; i < len; i++){
7461                     var l = ls[i];
7462                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7463                         this.firing = false;
7464                         return false;
7465                     }
7466                 }
7467                 this.firing = false;
7468             }
7469             return true;
7470         }
7471     };
7472 })();/*
7473  * RooJS Library 
7474  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7475  *
7476  * Licence LGPL 
7477  *
7478  */
7479  
7480 /**
7481  * @class Roo.Document
7482  * @extends Roo.util.Observable
7483  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7484  * 
7485  * @param {Object} config the methods and properties of the 'base' class for the application.
7486  * 
7487  *  Generic Page handler - implement this to start your app..
7488  * 
7489  * eg.
7490  *  MyProject = new Roo.Document({
7491         events : {
7492             'load' : true // your events..
7493         },
7494         listeners : {
7495             'ready' : function() {
7496                 // fired on Roo.onReady()
7497             }
7498         }
7499  * 
7500  */
7501 Roo.Document = function(cfg) {
7502      
7503     this.addEvents({ 
7504         'ready' : true
7505     });
7506     Roo.util.Observable.call(this,cfg);
7507     
7508     var _this = this;
7509     
7510     Roo.onReady(function() {
7511         _this.fireEvent('ready');
7512     },null,false);
7513     
7514     
7515 }
7516
7517 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7518  * Based on:
7519  * Ext JS Library 1.1.1
7520  * Copyright(c) 2006-2007, Ext JS, LLC.
7521  *
7522  * Originally Released Under LGPL - original licence link has changed is not relivant.
7523  *
7524  * Fork - LGPL
7525  * <script type="text/javascript">
7526  */
7527
7528 /**
7529  * @class Roo.EventManager
7530  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7531  * several useful events directly.
7532  * See {@link Roo.EventObject} for more details on normalized event objects.
7533  * @static
7534  */
7535 Roo.EventManager = function(){
7536     var docReadyEvent, docReadyProcId, docReadyState = false;
7537     var resizeEvent, resizeTask, textEvent, textSize;
7538     var E = Roo.lib.Event;
7539     var D = Roo.lib.Dom;
7540
7541     
7542     
7543
7544     var fireDocReady = function(){
7545         if(!docReadyState){
7546             docReadyState = true;
7547             Roo.isReady = true;
7548             if(docReadyProcId){
7549                 clearInterval(docReadyProcId);
7550             }
7551             if(Roo.isGecko || Roo.isOpera) {
7552                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7553             }
7554             if(Roo.isIE){
7555                 var defer = document.getElementById("ie-deferred-loader");
7556                 if(defer){
7557                     defer.onreadystatechange = null;
7558                     defer.parentNode.removeChild(defer);
7559                 }
7560             }
7561             if(docReadyEvent){
7562                 docReadyEvent.fire();
7563                 docReadyEvent.clearListeners();
7564             }
7565         }
7566     };
7567     
7568     var initDocReady = function(){
7569         docReadyEvent = new Roo.util.Event();
7570         if(Roo.isGecko || Roo.isOpera) {
7571             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7572         }else if(Roo.isIE){
7573             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7574             var defer = document.getElementById("ie-deferred-loader");
7575             defer.onreadystatechange = function(){
7576                 if(this.readyState == "complete"){
7577                     fireDocReady();
7578                 }
7579             };
7580         }else if(Roo.isSafari){ 
7581             docReadyProcId = setInterval(function(){
7582                 var rs = document.readyState;
7583                 if(rs == "complete") {
7584                     fireDocReady();     
7585                  }
7586             }, 10);
7587         }
7588         // no matter what, make sure it fires on load
7589         E.on(window, "load", fireDocReady);
7590     };
7591
7592     var createBuffered = function(h, o){
7593         var task = new Roo.util.DelayedTask(h);
7594         return function(e){
7595             // create new event object impl so new events don't wipe out properties
7596             e = new Roo.EventObjectImpl(e);
7597             task.delay(o.buffer, h, null, [e]);
7598         };
7599     };
7600
7601     var createSingle = function(h, el, ename, fn){
7602         return function(e){
7603             Roo.EventManager.removeListener(el, ename, fn);
7604             h(e);
7605         };
7606     };
7607
7608     var createDelayed = function(h, o){
7609         return function(e){
7610             // create new event object impl so new events don't wipe out properties
7611             e = new Roo.EventObjectImpl(e);
7612             setTimeout(function(){
7613                 h(e);
7614             }, o.delay || 10);
7615         };
7616     };
7617     var transitionEndVal = false;
7618     
7619     var transitionEnd = function()
7620     {
7621         if (transitionEndVal) {
7622             return transitionEndVal;
7623         }
7624         var el = document.createElement('div');
7625
7626         var transEndEventNames = {
7627             WebkitTransition : 'webkitTransitionEnd',
7628             MozTransition    : 'transitionend',
7629             OTransition      : 'oTransitionEnd otransitionend',
7630             transition       : 'transitionend'
7631         };
7632     
7633         for (var name in transEndEventNames) {
7634             if (el.style[name] !== undefined) {
7635                 transitionEndVal = transEndEventNames[name];
7636                 return  transitionEndVal ;
7637             }
7638         }
7639     }
7640     
7641   
7642
7643     var listen = function(element, ename, opt, fn, scope)
7644     {
7645         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7646         fn = fn || o.fn; scope = scope || o.scope;
7647         var el = Roo.getDom(element);
7648         
7649         
7650         if(!el){
7651             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7652         }
7653         
7654         if (ename == 'transitionend') {
7655             ename = transitionEnd();
7656         }
7657         var h = function(e){
7658             e = Roo.EventObject.setEvent(e);
7659             var t;
7660             if(o.delegate){
7661                 t = e.getTarget(o.delegate, el);
7662                 if(!t){
7663                     return;
7664                 }
7665             }else{
7666                 t = e.target;
7667             }
7668             if(o.stopEvent === true){
7669                 e.stopEvent();
7670             }
7671             if(o.preventDefault === true){
7672                e.preventDefault();
7673             }
7674             if(o.stopPropagation === true){
7675                 e.stopPropagation();
7676             }
7677
7678             if(o.normalized === false){
7679                 e = e.browserEvent;
7680             }
7681
7682             fn.call(scope || el, e, t, o);
7683         };
7684         if(o.delay){
7685             h = createDelayed(h, o);
7686         }
7687         if(o.single){
7688             h = createSingle(h, el, ename, fn);
7689         }
7690         if(o.buffer){
7691             h = createBuffered(h, o);
7692         }
7693         
7694         fn._handlers = fn._handlers || [];
7695         
7696         
7697         fn._handlers.push([Roo.id(el), ename, h]);
7698         
7699         
7700          
7701         E.on(el, ename, h); // this adds the actuall listener to the object..
7702         
7703         
7704         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7705             el.addEventListener("DOMMouseScroll", h, false);
7706             E.on(window, 'unload', function(){
7707                 el.removeEventListener("DOMMouseScroll", h, false);
7708             });
7709         }
7710         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7711             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7712         }
7713         return h;
7714     };
7715
7716     var stopListening = function(el, ename, fn){
7717         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7718         if(hds){
7719             for(var i = 0, len = hds.length; i < len; i++){
7720                 var h = hds[i];
7721                 if(h[0] == id && h[1] == ename){
7722                     hd = h[2];
7723                     hds.splice(i, 1);
7724                     break;
7725                 }
7726             }
7727         }
7728         E.un(el, ename, hd);
7729         el = Roo.getDom(el);
7730         if(ename == "mousewheel" && el.addEventListener){
7731             el.removeEventListener("DOMMouseScroll", hd, false);
7732         }
7733         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7734             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7735         }
7736     };
7737
7738     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7739     
7740     var pub = {
7741         
7742         
7743         /** 
7744          * Fix for doc tools
7745          * @scope Roo.EventManager
7746          */
7747         
7748         
7749         /** 
7750          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7751          * object with a Roo.EventObject
7752          * @param {Function} fn        The method the event invokes
7753          * @param {Object}   scope    An object that becomes the scope of the handler
7754          * @param {boolean}  override If true, the obj passed in becomes
7755          *                             the execution scope of the listener
7756          * @return {Function} The wrapped function
7757          * @deprecated
7758          */
7759         wrap : function(fn, scope, override){
7760             return function(e){
7761                 Roo.EventObject.setEvent(e);
7762                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7763             };
7764         },
7765         
7766         /**
7767      * Appends an event handler to an element (shorthand for addListener)
7768      * @param {String/HTMLElement}   element        The html element or id to assign the
7769      * @param {String}   eventName The type of event to listen for
7770      * @param {Function} handler The method the event invokes
7771      * @param {Object}   scope (optional) The scope in which to execute the handler
7772      * function. The handler function's "this" context.
7773      * @param {Object}   options (optional) An object containing handler configuration
7774      * properties. This may contain any of the following properties:<ul>
7775      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7776      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7777      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7778      * <li>preventDefault {Boolean} True to prevent the default action</li>
7779      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7780      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7781      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7782      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7783      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7784      * by the specified number of milliseconds. If the event fires again within that time, the original
7785      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7786      * </ul><br>
7787      * <p>
7788      * <b>Combining Options</b><br>
7789      * Using the options argument, it is possible to combine different types of listeners:<br>
7790      * <br>
7791      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7792      * Code:<pre><code>
7793 el.on('click', this.onClick, this, {
7794     single: true,
7795     delay: 100,
7796     stopEvent : true,
7797     forumId: 4
7798 });</code></pre>
7799      * <p>
7800      * <b>Attaching multiple handlers in 1 call</b><br>
7801       * The method also allows for a single argument to be passed which is a config object containing properties
7802      * which specify multiple handlers.
7803      * <p>
7804      * Code:<pre><code>
7805 el.on({
7806     'click' : {
7807         fn: this.onClick
7808         scope: this,
7809         delay: 100
7810     },
7811     'mouseover' : {
7812         fn: this.onMouseOver
7813         scope: this
7814     },
7815     'mouseout' : {
7816         fn: this.onMouseOut
7817         scope: this
7818     }
7819 });</code></pre>
7820      * <p>
7821      * Or a shorthand syntax:<br>
7822      * Code:<pre><code>
7823 el.on({
7824     'click' : this.onClick,
7825     'mouseover' : this.onMouseOver,
7826     'mouseout' : this.onMouseOut
7827     scope: this
7828 });</code></pre>
7829      */
7830         addListener : function(element, eventName, fn, scope, options){
7831             if(typeof eventName == "object"){
7832                 var o = eventName;
7833                 for(var e in o){
7834                     if(propRe.test(e)){
7835                         continue;
7836                     }
7837                     if(typeof o[e] == "function"){
7838                         // shared options
7839                         listen(element, e, o, o[e], o.scope);
7840                     }else{
7841                         // individual options
7842                         listen(element, e, o[e]);
7843                     }
7844                 }
7845                 return;
7846             }
7847             return listen(element, eventName, options, fn, scope);
7848         },
7849         
7850         /**
7851          * Removes an event handler
7852          *
7853          * @param {String/HTMLElement}   element        The id or html element to remove the 
7854          *                             event from
7855          * @param {String}   eventName     The type of event
7856          * @param {Function} fn
7857          * @return {Boolean} True if a listener was actually removed
7858          */
7859         removeListener : function(element, eventName, fn){
7860             return stopListening(element, eventName, fn);
7861         },
7862         
7863         /**
7864          * Fires when the document is ready (before onload and before images are loaded). Can be 
7865          * accessed shorthanded Roo.onReady().
7866          * @param {Function} fn        The method the event invokes
7867          * @param {Object}   scope    An  object that becomes the scope of the handler
7868          * @param {boolean}  options
7869          */
7870         onDocumentReady : function(fn, scope, options){
7871             if(docReadyState){ // if it already fired
7872                 docReadyEvent.addListener(fn, scope, options);
7873                 docReadyEvent.fire();
7874                 docReadyEvent.clearListeners();
7875                 return;
7876             }
7877             if(!docReadyEvent){
7878                 initDocReady();
7879             }
7880             docReadyEvent.addListener(fn, scope, options);
7881         },
7882         
7883         /**
7884          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7885          * @param {Function} fn        The method the event invokes
7886          * @param {Object}   scope    An object that becomes the scope of the handler
7887          * @param {boolean}  options
7888          */
7889         onWindowResize : function(fn, scope, options)
7890         {
7891             if(!resizeEvent){
7892                 resizeEvent = new Roo.util.Event();
7893                 resizeTask = new Roo.util.DelayedTask(function(){
7894                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7895                 });
7896                 E.on(window, "resize", function()
7897                 {
7898                     if (Roo.isIE) {
7899                         resizeTask.delay(50);
7900                     } else {
7901                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7902                     }
7903                 });
7904             }
7905             resizeEvent.addListener(fn, scope, options);
7906         },
7907
7908         /**
7909          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7910          * @param {Function} fn        The method the event invokes
7911          * @param {Object}   scope    An object that becomes the scope of the handler
7912          * @param {boolean}  options
7913          */
7914         onTextResize : function(fn, scope, options){
7915             if(!textEvent){
7916                 textEvent = new Roo.util.Event();
7917                 var textEl = new Roo.Element(document.createElement('div'));
7918                 textEl.dom.className = 'x-text-resize';
7919                 textEl.dom.innerHTML = 'X';
7920                 textEl.appendTo(document.body);
7921                 textSize = textEl.dom.offsetHeight;
7922                 setInterval(function(){
7923                     if(textEl.dom.offsetHeight != textSize){
7924                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7925                     }
7926                 }, this.textResizeInterval);
7927             }
7928             textEvent.addListener(fn, scope, options);
7929         },
7930
7931         /**
7932          * Removes the passed window resize listener.
7933          * @param {Function} fn        The method the event invokes
7934          * @param {Object}   scope    The scope of handler
7935          */
7936         removeResizeListener : function(fn, scope){
7937             if(resizeEvent){
7938                 resizeEvent.removeListener(fn, scope);
7939             }
7940         },
7941
7942         // private
7943         fireResize : function(){
7944             if(resizeEvent){
7945                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7946             }   
7947         },
7948         /**
7949          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7950          */
7951         ieDeferSrc : false,
7952         /**
7953          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7954          */
7955         textResizeInterval : 50
7956     };
7957     
7958     /**
7959      * Fix for doc tools
7960      * @scopeAlias pub=Roo.EventManager
7961      */
7962     
7963      /**
7964      * Appends an event handler to an element (shorthand for addListener)
7965      * @param {String/HTMLElement}   element        The html element or id to assign the
7966      * @param {String}   eventName The type of event to listen for
7967      * @param {Function} handler The method the event invokes
7968      * @param {Object}   scope (optional) The scope in which to execute the handler
7969      * function. The handler function's "this" context.
7970      * @param {Object}   options (optional) An object containing handler configuration
7971      * properties. This may contain any of the following properties:<ul>
7972      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7973      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7974      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7975      * <li>preventDefault {Boolean} True to prevent the default action</li>
7976      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7977      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7978      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7979      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7980      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7981      * by the specified number of milliseconds. If the event fires again within that time, the original
7982      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7983      * </ul><br>
7984      * <p>
7985      * <b>Combining Options</b><br>
7986      * Using the options argument, it is possible to combine different types of listeners:<br>
7987      * <br>
7988      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7989      * Code:<pre><code>
7990 el.on('click', this.onClick, this, {
7991     single: true,
7992     delay: 100,
7993     stopEvent : true,
7994     forumId: 4
7995 });</code></pre>
7996      * <p>
7997      * <b>Attaching multiple handlers in 1 call</b><br>
7998       * The method also allows for a single argument to be passed which is a config object containing properties
7999      * which specify multiple handlers.
8000      * <p>
8001      * Code:<pre><code>
8002 el.on({
8003     'click' : {
8004         fn: this.onClick
8005         scope: this,
8006         delay: 100
8007     },
8008     'mouseover' : {
8009         fn: this.onMouseOver
8010         scope: this
8011     },
8012     'mouseout' : {
8013         fn: this.onMouseOut
8014         scope: this
8015     }
8016 });</code></pre>
8017      * <p>
8018      * Or a shorthand syntax:<br>
8019      * Code:<pre><code>
8020 el.on({
8021     'click' : this.onClick,
8022     'mouseover' : this.onMouseOver,
8023     'mouseout' : this.onMouseOut
8024     scope: this
8025 });</code></pre>
8026      */
8027     pub.on = pub.addListener;
8028     pub.un = pub.removeListener;
8029
8030     pub.stoppedMouseDownEvent = new Roo.util.Event();
8031     return pub;
8032 }();
8033 /**
8034   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
8035   * @param {Function} fn        The method the event invokes
8036   * @param {Object}   scope    An  object that becomes the scope of the handler
8037   * @param {boolean}  override If true, the obj passed in becomes
8038   *                             the execution scope of the listener
8039   * @member Roo
8040   * @method onReady
8041  */
8042 Roo.onReady = Roo.EventManager.onDocumentReady;
8043
8044 Roo.onReady(function(){
8045     var bd = Roo.get(document.body);
8046     if(!bd){ return; }
8047
8048     var cls = [
8049             Roo.isIE ? "roo-ie"
8050             : Roo.isIE11 ? "roo-ie11"
8051             : Roo.isEdge ? "roo-edge"
8052             : Roo.isGecko ? "roo-gecko"
8053             : Roo.isOpera ? "roo-opera"
8054             : Roo.isSafari ? "roo-safari" : ""];
8055
8056     if(Roo.isMac){
8057         cls.push("roo-mac");
8058     }
8059     if(Roo.isLinux){
8060         cls.push("roo-linux");
8061     }
8062     if(Roo.isIOS){
8063         cls.push("roo-ios");
8064     }
8065     if(Roo.isTouch){
8066         cls.push("roo-touch");
8067     }
8068     if(Roo.isBorderBox){
8069         cls.push('roo-border-box');
8070     }
8071     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8072         var p = bd.dom.parentNode;
8073         if(p){
8074             p.className += ' roo-strict';
8075         }
8076     }
8077     bd.addClass(cls.join(' '));
8078 });
8079
8080 /**
8081  * @class Roo.EventObject
8082  * EventObject exposes the Yahoo! UI Event functionality directly on the object
8083  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
8084  * Example:
8085  * <pre><code>
8086  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8087     e.preventDefault();
8088     var target = e.getTarget();
8089     ...
8090  }
8091  var myDiv = Roo.get("myDiv");
8092  myDiv.on("click", handleClick);
8093  //or
8094  Roo.EventManager.on("myDiv", 'click', handleClick);
8095  Roo.EventManager.addListener("myDiv", 'click', handleClick);
8096  </code></pre>
8097  * @static
8098  */
8099 Roo.EventObject = function(){
8100     
8101     var E = Roo.lib.Event;
8102     
8103     // safari keypress events for special keys return bad keycodes
8104     var safariKeys = {
8105         63234 : 37, // left
8106         63235 : 39, // right
8107         63232 : 38, // up
8108         63233 : 40, // down
8109         63276 : 33, // page up
8110         63277 : 34, // page down
8111         63272 : 46, // delete
8112         63273 : 36, // home
8113         63275 : 35  // end
8114     };
8115
8116     // normalize button clicks
8117     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8118                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8119
8120     Roo.EventObjectImpl = function(e){
8121         if(e){
8122             this.setEvent(e.browserEvent || e);
8123         }
8124     };
8125     Roo.EventObjectImpl.prototype = {
8126         /**
8127          * Used to fix doc tools.
8128          * @scope Roo.EventObject.prototype
8129          */
8130             
8131
8132         
8133         
8134         /** The normal browser event */
8135         browserEvent : null,
8136         /** The button pressed in a mouse event */
8137         button : -1,
8138         /** True if the shift key was down during the event */
8139         shiftKey : false,
8140         /** True if the control key was down during the event */
8141         ctrlKey : false,
8142         /** True if the alt key was down during the event */
8143         altKey : false,
8144
8145         /** Key constant 
8146         * @type Number */
8147         BACKSPACE : 8,
8148         /** Key constant 
8149         * @type Number */
8150         TAB : 9,
8151         /** Key constant 
8152         * @type Number */
8153         RETURN : 13,
8154         /** Key constant 
8155         * @type Number */
8156         ENTER : 13,
8157         /** Key constant 
8158         * @type Number */
8159         SHIFT : 16,
8160         /** Key constant 
8161         * @type Number */
8162         CONTROL : 17,
8163         /** Key constant 
8164         * @type Number */
8165         ESC : 27,
8166         /** Key constant 
8167         * @type Number */
8168         SPACE : 32,
8169         /** Key constant 
8170         * @type Number */
8171         PAGEUP : 33,
8172         /** Key constant 
8173         * @type Number */
8174         PAGEDOWN : 34,
8175         /** Key constant 
8176         * @type Number */
8177         END : 35,
8178         /** Key constant 
8179         * @type Number */
8180         HOME : 36,
8181         /** Key constant 
8182         * @type Number */
8183         LEFT : 37,
8184         /** Key constant 
8185         * @type Number */
8186         UP : 38,
8187         /** Key constant 
8188         * @type Number */
8189         RIGHT : 39,
8190         /** Key constant 
8191         * @type Number */
8192         DOWN : 40,
8193         /** Key constant 
8194         * @type Number */
8195         DELETE : 46,
8196         /** Key constant 
8197         * @type Number */
8198         F5 : 116,
8199
8200            /** @private */
8201         setEvent : function(e){
8202             if(e == this || (e && e.browserEvent)){ // already wrapped
8203                 return e;
8204             }
8205             this.browserEvent = e;
8206             if(e){
8207                 // normalize buttons
8208                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8209                 if(e.type == 'click' && this.button == -1){
8210                     this.button = 0;
8211                 }
8212                 this.type = e.type;
8213                 this.shiftKey = e.shiftKey;
8214                 // mac metaKey behaves like ctrlKey
8215                 this.ctrlKey = e.ctrlKey || e.metaKey;
8216                 this.altKey = e.altKey;
8217                 // in getKey these will be normalized for the mac
8218                 this.keyCode = e.keyCode;
8219                 // keyup warnings on firefox.
8220                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8221                 // cache the target for the delayed and or buffered events
8222                 this.target = E.getTarget(e);
8223                 // same for XY
8224                 this.xy = E.getXY(e);
8225             }else{
8226                 this.button = -1;
8227                 this.shiftKey = false;
8228                 this.ctrlKey = false;
8229                 this.altKey = false;
8230                 this.keyCode = 0;
8231                 this.charCode =0;
8232                 this.target = null;
8233                 this.xy = [0, 0];
8234             }
8235             return this;
8236         },
8237
8238         /**
8239          * Stop the event (preventDefault and stopPropagation)
8240          */
8241         stopEvent : function(){
8242             if(this.browserEvent){
8243                 if(this.browserEvent.type == 'mousedown'){
8244                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8245                 }
8246                 E.stopEvent(this.browserEvent);
8247             }
8248         },
8249
8250         /**
8251          * Prevents the browsers default handling of the event.
8252          */
8253         preventDefault : function(){
8254             if(this.browserEvent){
8255                 E.preventDefault(this.browserEvent);
8256             }
8257         },
8258
8259         /** @private */
8260         isNavKeyPress : function(){
8261             var k = this.keyCode;
8262             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8263             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8264         },
8265
8266         isSpecialKey : function(){
8267             var k = this.keyCode;
8268             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8269             (k == 16) || (k == 17) ||
8270             (k >= 18 && k <= 20) ||
8271             (k >= 33 && k <= 35) ||
8272             (k >= 36 && k <= 39) ||
8273             (k >= 44 && k <= 45);
8274         },
8275         /**
8276          * Cancels bubbling of the event.
8277          */
8278         stopPropagation : function(){
8279             if(this.browserEvent){
8280                 if(this.type == 'mousedown'){
8281                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8282                 }
8283                 E.stopPropagation(this.browserEvent);
8284             }
8285         },
8286
8287         /**
8288          * Gets the key code for the event.
8289          * @return {Number}
8290          */
8291         getCharCode : function(){
8292             return this.charCode || this.keyCode;
8293         },
8294
8295         /**
8296          * Returns a normalized keyCode for the event.
8297          * @return {Number} The key code
8298          */
8299         getKey : function(){
8300             var k = this.keyCode || this.charCode;
8301             return Roo.isSafari ? (safariKeys[k] || k) : k;
8302         },
8303
8304         /**
8305          * Gets the x coordinate of the event.
8306          * @return {Number}
8307          */
8308         getPageX : function(){
8309             return this.xy[0];
8310         },
8311
8312         /**
8313          * Gets the y coordinate of the event.
8314          * @return {Number}
8315          */
8316         getPageY : function(){
8317             return this.xy[1];
8318         },
8319
8320         /**
8321          * Gets the time of the event.
8322          * @return {Number}
8323          */
8324         getTime : function(){
8325             if(this.browserEvent){
8326                 return E.getTime(this.browserEvent);
8327             }
8328             return null;
8329         },
8330
8331         /**
8332          * Gets the page coordinates of the event.
8333          * @return {Array} The xy values like [x, y]
8334          */
8335         getXY : function(){
8336             return this.xy;
8337         },
8338
8339         /**
8340          * Gets the target for the event.
8341          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8342          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8343                 search as a number or element (defaults to 10 || document.body)
8344          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8345          * @return {HTMLelement}
8346          */
8347         getTarget : function(selector, maxDepth, returnEl){
8348             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8349         },
8350         /**
8351          * Gets the related target.
8352          * @return {HTMLElement}
8353          */
8354         getRelatedTarget : function(){
8355             if(this.browserEvent){
8356                 return E.getRelatedTarget(this.browserEvent);
8357             }
8358             return null;
8359         },
8360
8361         /**
8362          * Normalizes mouse wheel delta across browsers
8363          * @return {Number} The delta
8364          */
8365         getWheelDelta : function(){
8366             var e = this.browserEvent;
8367             var delta = 0;
8368             if(e.wheelDelta){ /* IE/Opera. */
8369                 delta = e.wheelDelta/120;
8370             }else if(e.detail){ /* Mozilla case. */
8371                 delta = -e.detail/3;
8372             }
8373             return delta;
8374         },
8375
8376         /**
8377          * Returns true if the control, meta, shift or alt key was pressed during this event.
8378          * @return {Boolean}
8379          */
8380         hasModifier : function(){
8381             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8382         },
8383
8384         /**
8385          * Returns true if the target of this event equals el or is a child of el
8386          * @param {String/HTMLElement/Element} el
8387          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8388          * @return {Boolean}
8389          */
8390         within : function(el, related){
8391             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8392             return t && Roo.fly(el).contains(t);
8393         },
8394
8395         getPoint : function(){
8396             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8397         }
8398     };
8399
8400     return new Roo.EventObjectImpl();
8401 }();
8402             
8403     /*
8404  * Based on:
8405  * Ext JS Library 1.1.1
8406  * Copyright(c) 2006-2007, Ext JS, LLC.
8407  *
8408  * Originally Released Under LGPL - original licence link has changed is not relivant.
8409  *
8410  * Fork - LGPL
8411  * <script type="text/javascript">
8412  */
8413
8414  
8415 // was in Composite Element!??!?!
8416  
8417 (function(){
8418     var D = Roo.lib.Dom;
8419     var E = Roo.lib.Event;
8420     var A = Roo.lib.Anim;
8421
8422     // local style camelizing for speed
8423     var propCache = {};
8424     var camelRe = /(-[a-z])/gi;
8425     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8426     var view = document.defaultView;
8427
8428 /**
8429  * @class Roo.Element
8430  * Represents an Element in the DOM.<br><br>
8431  * Usage:<br>
8432 <pre><code>
8433 var el = Roo.get("my-div");
8434
8435 // or with getEl
8436 var el = getEl("my-div");
8437
8438 // or with a DOM element
8439 var el = Roo.get(myDivElement);
8440 </code></pre>
8441  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8442  * each call instead of constructing a new one.<br><br>
8443  * <b>Animations</b><br />
8444  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8445  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8446 <pre>
8447 Option    Default   Description
8448 --------- --------  ---------------------------------------------
8449 duration  .35       The duration of the animation in seconds
8450 easing    easeOut   The YUI easing method
8451 callback  none      A function to execute when the anim completes
8452 scope     this      The scope (this) of the callback function
8453 </pre>
8454 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8455 * manipulate the animation. Here's an example:
8456 <pre><code>
8457 var el = Roo.get("my-div");
8458
8459 // no animation
8460 el.setWidth(100);
8461
8462 // default animation
8463 el.setWidth(100, true);
8464
8465 // animation with some options set
8466 el.setWidth(100, {
8467     duration: 1,
8468     callback: this.foo,
8469     scope: this
8470 });
8471
8472 // using the "anim" property to get the Anim object
8473 var opt = {
8474     duration: 1,
8475     callback: this.foo,
8476     scope: this
8477 };
8478 el.setWidth(100, opt);
8479 ...
8480 if(opt.anim.isAnimated()){
8481     opt.anim.stop();
8482 }
8483 </code></pre>
8484 * <b> Composite (Collections of) Elements</b><br />
8485  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8486  * @constructor Create a new Element directly.
8487  * @param {String/HTMLElement} element
8488  * @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).
8489  */
8490     Roo.Element = function(element, forceNew)
8491     {
8492         var dom = typeof element == "string" ?
8493                 document.getElementById(element) : element;
8494         
8495         this.listeners = {};
8496         
8497         if(!dom){ // invalid id/element
8498             return null;
8499         }
8500         var id = dom.id;
8501         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8502             return Roo.Element.cache[id];
8503         }
8504
8505         /**
8506          * The DOM element
8507          * @type HTMLElement
8508          */
8509         this.dom = dom;
8510
8511         /**
8512          * The DOM element ID
8513          * @type String
8514          */
8515         this.id = id || Roo.id(dom);
8516         
8517         return this; // assumed for cctor?
8518     };
8519
8520     var El = Roo.Element;
8521
8522     El.prototype = {
8523         /**
8524          * The element's default display mode  (defaults to "") 
8525          * @type String
8526          */
8527         originalDisplay : "",
8528
8529         
8530         // note this is overridden in BS version..
8531         visibilityMode : 1, 
8532         /**
8533          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8534          * @type String
8535          */
8536         defaultUnit : "px",
8537         
8538         /**
8539          * Sets the element's visibility mode. When setVisible() is called it
8540          * will use this to determine whether to set the visibility or the display property.
8541          * @param visMode Element.VISIBILITY or Element.DISPLAY
8542          * @return {Roo.Element} this
8543          */
8544         setVisibilityMode : function(visMode){
8545             this.visibilityMode = visMode;
8546             return this;
8547         },
8548         /**
8549          * Convenience method for setVisibilityMode(Element.DISPLAY)
8550          * @param {String} display (optional) What to set display to when visible
8551          * @return {Roo.Element} this
8552          */
8553         enableDisplayMode : function(display){
8554             this.setVisibilityMode(El.DISPLAY);
8555             if(typeof display != "undefined") { this.originalDisplay = display; }
8556             return this;
8557         },
8558
8559         /**
8560          * 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)
8561          * @param {String} selector The simple selector to test
8562          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8563                 search as a number or element (defaults to 10 || document.body)
8564          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8565          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8566          */
8567         findParent : function(simpleSelector, maxDepth, returnEl){
8568             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8569             maxDepth = maxDepth || 50;
8570             if(typeof maxDepth != "number"){
8571                 stopEl = Roo.getDom(maxDepth);
8572                 maxDepth = 10;
8573             }
8574             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8575                 if(dq.is(p, simpleSelector)){
8576                     return returnEl ? Roo.get(p) : p;
8577                 }
8578                 depth++;
8579                 p = p.parentNode;
8580             }
8581             return null;
8582         },
8583
8584
8585         /**
8586          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8587          * @param {String} selector The simple selector to test
8588          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8589                 search as a number or element (defaults to 10 || document.body)
8590          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8591          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8592          */
8593         findParentNode : function(simpleSelector, maxDepth, returnEl){
8594             var p = Roo.fly(this.dom.parentNode, '_internal');
8595             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8596         },
8597         
8598         /**
8599          * Looks at  the scrollable parent element
8600          */
8601         findScrollableParent : function()
8602         {
8603             var overflowRegex = /(auto|scroll)/;
8604             
8605             if(this.getStyle('position') === 'fixed'){
8606                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8607             }
8608             
8609             var excludeStaticParent = this.getStyle('position') === "absolute";
8610             
8611             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8612                 
8613                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8614                     continue;
8615                 }
8616                 
8617                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8618                     return parent;
8619                 }
8620                 
8621                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8622                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8623                 }
8624             }
8625             
8626             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8627         },
8628
8629         /**
8630          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8631          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8632          * @param {String} selector The simple selector to test
8633          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8634                 search as a number or element (defaults to 10 || document.body)
8635          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8636          */
8637         up : function(simpleSelector, maxDepth){
8638             return this.findParentNode(simpleSelector, maxDepth, true);
8639         },
8640
8641
8642
8643         /**
8644          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8645          * @param {String} selector The simple selector to test
8646          * @return {Boolean} True if this element matches the selector, else false
8647          */
8648         is : function(simpleSelector){
8649             return Roo.DomQuery.is(this.dom, simpleSelector);
8650         },
8651
8652         /**
8653          * Perform animation on this element.
8654          * @param {Object} args The YUI animation control args
8655          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8656          * @param {Function} onComplete (optional) Function to call when animation completes
8657          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8658          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8659          * @return {Roo.Element} this
8660          */
8661         animate : function(args, duration, onComplete, easing, animType){
8662             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8663             return this;
8664         },
8665
8666         /*
8667          * @private Internal animation call
8668          */
8669         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8670             animType = animType || 'run';
8671             opt = opt || {};
8672             var anim = Roo.lib.Anim[animType](
8673                 this.dom, args,
8674                 (opt.duration || defaultDur) || .35,
8675                 (opt.easing || defaultEase) || 'easeOut',
8676                 function(){
8677                     Roo.callback(cb, this);
8678                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8679                 },
8680                 this
8681             );
8682             opt.anim = anim;
8683             return anim;
8684         },
8685
8686         // private legacy anim prep
8687         preanim : function(a, i){
8688             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8689         },
8690
8691         /**
8692          * Removes worthless text nodes
8693          * @param {Boolean} forceReclean (optional) By default the element
8694          * keeps track if it has been cleaned already so
8695          * you can call this over and over. However, if you update the element and
8696          * need to force a reclean, you can pass true.
8697          */
8698         clean : function(forceReclean){
8699             if(this.isCleaned && forceReclean !== true){
8700                 return this;
8701             }
8702             var ns = /\S/;
8703             var d = this.dom, n = d.firstChild, ni = -1;
8704             while(n){
8705                 var nx = n.nextSibling;
8706                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8707                     d.removeChild(n);
8708                 }else{
8709                     n.nodeIndex = ++ni;
8710                 }
8711                 n = nx;
8712             }
8713             this.isCleaned = true;
8714             return this;
8715         },
8716
8717         // private
8718         calcOffsetsTo : function(el){
8719             el = Roo.get(el);
8720             var d = el.dom;
8721             var restorePos = false;
8722             if(el.getStyle('position') == 'static'){
8723                 el.position('relative');
8724                 restorePos = true;
8725             }
8726             var x = 0, y =0;
8727             var op = this.dom;
8728             while(op && op != d && op.tagName != 'HTML'){
8729                 x+= op.offsetLeft;
8730                 y+= op.offsetTop;
8731                 op = op.offsetParent;
8732             }
8733             if(restorePos){
8734                 el.position('static');
8735             }
8736             return [x, y];
8737         },
8738
8739         /**
8740          * Scrolls this element into view within the passed container.
8741          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8742          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8743          * @return {Roo.Element} this
8744          */
8745         scrollIntoView : function(container, hscroll){
8746             var c = Roo.getDom(container) || document.body;
8747             var el = this.dom;
8748
8749             var o = this.calcOffsetsTo(c),
8750                 l = o[0],
8751                 t = o[1],
8752                 b = t+el.offsetHeight,
8753                 r = l+el.offsetWidth;
8754
8755             var ch = c.clientHeight;
8756             var ct = parseInt(c.scrollTop, 10);
8757             var cl = parseInt(c.scrollLeft, 10);
8758             var cb = ct + ch;
8759             var cr = cl + c.clientWidth;
8760
8761             if(t < ct){
8762                 c.scrollTop = t;
8763             }else if(b > cb){
8764                 c.scrollTop = b-ch;
8765             }
8766
8767             if(hscroll !== false){
8768                 if(l < cl){
8769                     c.scrollLeft = l;
8770                 }else if(r > cr){
8771                     c.scrollLeft = r-c.clientWidth;
8772                 }
8773             }
8774             return this;
8775         },
8776
8777         // private
8778         scrollChildIntoView : function(child, hscroll){
8779             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8780         },
8781
8782         /**
8783          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8784          * the new height may not be available immediately.
8785          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8786          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8787          * @param {Function} onComplete (optional) Function to call when animation completes
8788          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8789          * @return {Roo.Element} this
8790          */
8791         autoHeight : function(animate, duration, onComplete, easing){
8792             var oldHeight = this.getHeight();
8793             this.clip();
8794             this.setHeight(1); // force clipping
8795             setTimeout(function(){
8796                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8797                 if(!animate){
8798                     this.setHeight(height);
8799                     this.unclip();
8800                     if(typeof onComplete == "function"){
8801                         onComplete();
8802                     }
8803                 }else{
8804                     this.setHeight(oldHeight); // restore original height
8805                     this.setHeight(height, animate, duration, function(){
8806                         this.unclip();
8807                         if(typeof onComplete == "function") { onComplete(); }
8808                     }.createDelegate(this), easing);
8809                 }
8810             }.createDelegate(this), 0);
8811             return this;
8812         },
8813
8814         /**
8815          * Returns true if this element is an ancestor of the passed element
8816          * @param {HTMLElement/String} el The element to check
8817          * @return {Boolean} True if this element is an ancestor of el, else false
8818          */
8819         contains : function(el){
8820             if(!el){return false;}
8821             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8822         },
8823
8824         /**
8825          * Checks whether the element is currently visible using both visibility and display properties.
8826          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8827          * @return {Boolean} True if the element is currently visible, else false
8828          */
8829         isVisible : function(deep) {
8830             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8831             if(deep !== true || !vis){
8832                 return vis;
8833             }
8834             var p = this.dom.parentNode;
8835             while(p && p.tagName.toLowerCase() != "body"){
8836                 if(!Roo.fly(p, '_isVisible').isVisible()){
8837                     return false;
8838                 }
8839                 p = p.parentNode;
8840             }
8841             return true;
8842         },
8843
8844         /**
8845          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8846          * @param {String} selector The CSS selector
8847          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8848          * @return {CompositeElement/CompositeElementLite} The composite element
8849          */
8850         select : function(selector, unique){
8851             return El.select(selector, unique, this.dom);
8852         },
8853
8854         /**
8855          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8856          * @param {String} selector The CSS selector
8857          * @return {Array} An array of the matched nodes
8858          */
8859         query : function(selector, unique){
8860             return Roo.DomQuery.select(selector, this.dom);
8861         },
8862
8863         /**
8864          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8865          * @param {String} selector The CSS selector
8866          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8867          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8868          */
8869         child : function(selector, returnDom){
8870             var n = Roo.DomQuery.selectNode(selector, this.dom);
8871             return returnDom ? n : Roo.get(n);
8872         },
8873
8874         /**
8875          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8876          * @param {String} selector The CSS selector
8877          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8878          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8879          */
8880         down : function(selector, returnDom){
8881             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8882             return returnDom ? n : Roo.get(n);
8883         },
8884
8885         /**
8886          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8887          * @param {String} group The group the DD object is member of
8888          * @param {Object} config The DD config object
8889          * @param {Object} overrides An object containing methods to override/implement on the DD object
8890          * @return {Roo.dd.DD} The DD object
8891          */
8892         initDD : function(group, config, overrides){
8893             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8894             return Roo.apply(dd, overrides);
8895         },
8896
8897         /**
8898          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8899          * @param {String} group The group the DDProxy object is member of
8900          * @param {Object} config The DDProxy config object
8901          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8902          * @return {Roo.dd.DDProxy} The DDProxy object
8903          */
8904         initDDProxy : function(group, config, overrides){
8905             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8906             return Roo.apply(dd, overrides);
8907         },
8908
8909         /**
8910          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8911          * @param {String} group The group the DDTarget object is member of
8912          * @param {Object} config The DDTarget config object
8913          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8914          * @return {Roo.dd.DDTarget} The DDTarget object
8915          */
8916         initDDTarget : function(group, config, overrides){
8917             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8918             return Roo.apply(dd, overrides);
8919         },
8920
8921         /**
8922          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8923          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8924          * @param {Boolean} visible Whether the element is visible
8925          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8926          * @return {Roo.Element} this
8927          */
8928          setVisible : function(visible, animate){
8929             if(!animate || !A){
8930                 if(this.visibilityMode == El.DISPLAY){
8931                     this.setDisplayed(visible);
8932                 }else{
8933                     this.fixDisplay();
8934                     this.dom.style.visibility = visible ? "visible" : "hidden";
8935                 }
8936             }else{
8937                 // closure for composites
8938                 var dom = this.dom;
8939                 var visMode = this.visibilityMode;
8940                 if(visible){
8941                     this.setOpacity(.01);
8942                     this.setVisible(true);
8943                 }
8944                 this.anim({opacity: { to: (visible?1:0) }},
8945                       this.preanim(arguments, 1),
8946                       null, .35, 'easeIn', function(){
8947                          if(!visible){
8948                              if(visMode == El.DISPLAY){
8949                                  dom.style.display = "none";
8950                              }else{
8951                                  dom.style.visibility = "hidden";
8952                              }
8953                              Roo.get(dom).setOpacity(1);
8954                          }
8955                      });
8956             }
8957             return this;
8958         },
8959
8960         /**
8961          * Returns true if display is not "none"
8962          * @return {Boolean}
8963          */
8964         isDisplayed : function() {
8965             return this.getStyle("display") != "none";
8966         },
8967
8968         /**
8969          * Toggles the element's visibility or display, depending on visibility mode.
8970          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8971          * @return {Roo.Element} this
8972          */
8973         toggle : function(animate){
8974             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8975             return this;
8976         },
8977
8978         /**
8979          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8980          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8981          * @return {Roo.Element} this
8982          */
8983         setDisplayed : function(value) {
8984             if(typeof value == "boolean"){
8985                value = value ? this.originalDisplay : "none";
8986             }
8987             this.setStyle("display", value);
8988             return this;
8989         },
8990
8991         /**
8992          * Tries to focus the element. Any exceptions are caught and ignored.
8993          * @return {Roo.Element} this
8994          */
8995         focus : function() {
8996             try{
8997                 this.dom.focus();
8998             }catch(e){}
8999             return this;
9000         },
9001
9002         /**
9003          * Tries to blur the element. Any exceptions are caught and ignored.
9004          * @return {Roo.Element} this
9005          */
9006         blur : function() {
9007             try{
9008                 this.dom.blur();
9009             }catch(e){}
9010             return this;
9011         },
9012
9013         /**
9014          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9015          * @param {String/Array} className The CSS class to add, or an array of classes
9016          * @return {Roo.Element} this
9017          */
9018         addClass : function(className){
9019             if(className instanceof Array){
9020                 for(var i = 0, len = className.length; i < len; i++) {
9021                     this.addClass(className[i]);
9022                 }
9023             }else{
9024                 if(className && !this.hasClass(className)){
9025                     if (this.dom instanceof SVGElement) {
9026                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
9027                     } else {
9028                         this.dom.className = this.dom.className + " " + className;
9029                     }
9030                 }
9031             }
9032             return this;
9033         },
9034
9035         /**
9036          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9037          * @param {String/Array} className The CSS class to add, or an array of classes
9038          * @return {Roo.Element} this
9039          */
9040         radioClass : function(className){
9041             var siblings = this.dom.parentNode.childNodes;
9042             for(var i = 0; i < siblings.length; i++) {
9043                 var s = siblings[i];
9044                 if(s.nodeType == 1){
9045                     Roo.get(s).removeClass(className);
9046                 }
9047             }
9048             this.addClass(className);
9049             return this;
9050         },
9051
9052         /**
9053          * Removes one or more CSS classes from the element.
9054          * @param {String/Array} className The CSS class to remove, or an array of classes
9055          * @return {Roo.Element} this
9056          */
9057         removeClass : function(className){
9058             
9059             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9060             if(!className || !cn){
9061                 return this;
9062             }
9063             if(className instanceof Array){
9064                 for(var i = 0, len = className.length; i < len; i++) {
9065                     this.removeClass(className[i]);
9066                 }
9067             }else{
9068                 if(this.hasClass(className)){
9069                     var re = this.classReCache[className];
9070                     if (!re) {
9071                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9072                        this.classReCache[className] = re;
9073                     }
9074                     if (this.dom instanceof SVGElement) {
9075                         this.dom.className.baseVal = cn.replace(re, " ");
9076                     } else {
9077                         this.dom.className = cn.replace(re, " ");
9078                     }
9079                 }
9080             }
9081             return this;
9082         },
9083
9084         // private
9085         classReCache: {},
9086
9087         /**
9088          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9089          * @param {String} className The CSS class to toggle
9090          * @return {Roo.Element} this
9091          */
9092         toggleClass : function(className){
9093             if(this.hasClass(className)){
9094                 this.removeClass(className);
9095             }else{
9096                 this.addClass(className);
9097             }
9098             return this;
9099         },
9100
9101         /**
9102          * Checks if the specified CSS class exists on this element's DOM node.
9103          * @param {String} className The CSS class to check for
9104          * @return {Boolean} True if the class exists, else false
9105          */
9106         hasClass : function(className){
9107             if (this.dom instanceof SVGElement) {
9108                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
9109             } 
9110             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9111         },
9112
9113         /**
9114          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
9115          * @param {String} oldClassName The CSS class to replace
9116          * @param {String} newClassName The replacement CSS class
9117          * @return {Roo.Element} this
9118          */
9119         replaceClass : function(oldClassName, newClassName){
9120             this.removeClass(oldClassName);
9121             this.addClass(newClassName);
9122             return this;
9123         },
9124
9125         /**
9126          * Returns an object with properties matching the styles requested.
9127          * For example, el.getStyles('color', 'font-size', 'width') might return
9128          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9129          * @param {String} style1 A style name
9130          * @param {String} style2 A style name
9131          * @param {String} etc.
9132          * @return {Object} The style object
9133          */
9134         getStyles : function(){
9135             var a = arguments, len = a.length, r = {};
9136             for(var i = 0; i < len; i++){
9137                 r[a[i]] = this.getStyle(a[i]);
9138             }
9139             return r;
9140         },
9141
9142         /**
9143          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9144          * @param {String} property The style property whose value is returned.
9145          * @return {String} The current value of the style property for this element.
9146          */
9147         getStyle : function(){
9148             return view && view.getComputedStyle ?
9149                 function(prop){
9150                     var el = this.dom, v, cs, camel;
9151                     if(prop == 'float'){
9152                         prop = "cssFloat";
9153                     }
9154                     if(el.style && (v = el.style[prop])){
9155                         return v;
9156                     }
9157                     if(cs = view.getComputedStyle(el, "")){
9158                         if(!(camel = propCache[prop])){
9159                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
9160                         }
9161                         return cs[camel];
9162                     }
9163                     return null;
9164                 } :
9165                 function(prop){
9166                     var el = this.dom, v, cs, camel;
9167                     if(prop == 'opacity'){
9168                         if(typeof el.style.filter == 'string'){
9169                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9170                             if(m){
9171                                 var fv = parseFloat(m[1]);
9172                                 if(!isNaN(fv)){
9173                                     return fv ? fv / 100 : 0;
9174                                 }
9175                             }
9176                         }
9177                         return 1;
9178                     }else if(prop == 'float'){
9179                         prop = "styleFloat";
9180                     }
9181                     if(!(camel = propCache[prop])){
9182                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
9183                     }
9184                     if(v = el.style[camel]){
9185                         return v;
9186                     }
9187                     if(cs = el.currentStyle){
9188                         return cs[camel];
9189                     }
9190                     return null;
9191                 };
9192         }(),
9193
9194         /**
9195          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9196          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9197          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9198          * @return {Roo.Element} this
9199          */
9200         setStyle : function(prop, value){
9201             if(typeof prop == "string"){
9202                 
9203                 if (prop == 'float') {
9204                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9205                     return this;
9206                 }
9207                 
9208                 var camel;
9209                 if(!(camel = propCache[prop])){
9210                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9211                 }
9212                 
9213                 if(camel == 'opacity') {
9214                     this.setOpacity(value);
9215                 }else{
9216                     this.dom.style[camel] = value;
9217                 }
9218             }else{
9219                 for(var style in prop){
9220                     if(typeof prop[style] != "function"){
9221                        this.setStyle(style, prop[style]);
9222                     }
9223                 }
9224             }
9225             return this;
9226         },
9227
9228         /**
9229          * More flexible version of {@link #setStyle} for setting style properties.
9230          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9231          * a function which returns such a specification.
9232          * @return {Roo.Element} this
9233          */
9234         applyStyles : function(style){
9235             Roo.DomHelper.applyStyles(this.dom, style);
9236             return this;
9237         },
9238
9239         /**
9240           * 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).
9241           * @return {Number} The X position of the element
9242           */
9243         getX : function(){
9244             return D.getX(this.dom);
9245         },
9246
9247         /**
9248           * 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).
9249           * @return {Number} The Y position of the element
9250           */
9251         getY : function(){
9252             return D.getY(this.dom);
9253         },
9254
9255         /**
9256           * 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).
9257           * @return {Array} The XY position of the element
9258           */
9259         getXY : function(){
9260             return D.getXY(this.dom);
9261         },
9262
9263         /**
9264          * 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).
9265          * @param {Number} The X position of the element
9266          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9267          * @return {Roo.Element} this
9268          */
9269         setX : function(x, animate){
9270             if(!animate || !A){
9271                 D.setX(this.dom, x);
9272             }else{
9273                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9274             }
9275             return this;
9276         },
9277
9278         /**
9279          * 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).
9280          * @param {Number} The Y position of the element
9281          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9282          * @return {Roo.Element} this
9283          */
9284         setY : function(y, animate){
9285             if(!animate || !A){
9286                 D.setY(this.dom, y);
9287             }else{
9288                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9289             }
9290             return this;
9291         },
9292
9293         /**
9294          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9295          * @param {String} left The left CSS property value
9296          * @return {Roo.Element} this
9297          */
9298         setLeft : function(left){
9299             this.setStyle("left", this.addUnits(left));
9300             return this;
9301         },
9302
9303         /**
9304          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9305          * @param {String} top The top CSS property value
9306          * @return {Roo.Element} this
9307          */
9308         setTop : function(top){
9309             this.setStyle("top", this.addUnits(top));
9310             return this;
9311         },
9312
9313         /**
9314          * Sets the element's CSS right style.
9315          * @param {String} right The right CSS property value
9316          * @return {Roo.Element} this
9317          */
9318         setRight : function(right){
9319             this.setStyle("right", this.addUnits(right));
9320             return this;
9321         },
9322
9323         /**
9324          * Sets the element's CSS bottom style.
9325          * @param {String} bottom The bottom CSS property value
9326          * @return {Roo.Element} this
9327          */
9328         setBottom : function(bottom){
9329             this.setStyle("bottom", this.addUnits(bottom));
9330             return this;
9331         },
9332
9333         /**
9334          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9335          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9336          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9337          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9338          * @return {Roo.Element} this
9339          */
9340         setXY : function(pos, animate){
9341             if(!animate || !A){
9342                 D.setXY(this.dom, pos);
9343             }else{
9344                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9345             }
9346             return this;
9347         },
9348
9349         /**
9350          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9351          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9352          * @param {Number} x X value for new position (coordinates are page-based)
9353          * @param {Number} y Y value for new position (coordinates are page-based)
9354          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9355          * @return {Roo.Element} this
9356          */
9357         setLocation : function(x, y, animate){
9358             this.setXY([x, y], this.preanim(arguments, 2));
9359             return this;
9360         },
9361
9362         /**
9363          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9364          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9365          * @param {Number} x X value for new position (coordinates are page-based)
9366          * @param {Number} y Y value for new position (coordinates are page-based)
9367          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9368          * @return {Roo.Element} this
9369          */
9370         moveTo : function(x, y, animate){
9371             this.setXY([x, y], this.preanim(arguments, 2));
9372             return this;
9373         },
9374
9375         /**
9376          * Returns the region of the given element.
9377          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9378          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9379          */
9380         getRegion : function(){
9381             return D.getRegion(this.dom);
9382         },
9383
9384         /**
9385          * Returns the offset height of the element
9386          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9387          * @return {Number} The element's height
9388          */
9389         getHeight : function(contentHeight){
9390             var h = this.dom.offsetHeight || 0;
9391             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9392         },
9393
9394         /**
9395          * Returns the offset width of the element
9396          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9397          * @return {Number} The element's width
9398          */
9399         getWidth : function(contentWidth){
9400             var w = this.dom.offsetWidth || 0;
9401             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9402         },
9403
9404         /**
9405          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9406          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9407          * if a height has not been set using CSS.
9408          * @return {Number}
9409          */
9410         getComputedHeight : function(){
9411             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9412             if(!h){
9413                 h = parseInt(this.getStyle('height'), 10) || 0;
9414                 if(!this.isBorderBox()){
9415                     h += this.getFrameWidth('tb');
9416                 }
9417             }
9418             return h;
9419         },
9420
9421         /**
9422          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9423          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9424          * if a width has not been set using CSS.
9425          * @return {Number}
9426          */
9427         getComputedWidth : function(){
9428             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9429             if(!w){
9430                 w = parseInt(this.getStyle('width'), 10) || 0;
9431                 if(!this.isBorderBox()){
9432                     w += this.getFrameWidth('lr');
9433                 }
9434             }
9435             return w;
9436         },
9437
9438         /**
9439          * Returns the size of the element.
9440          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9441          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9442          */
9443         getSize : function(contentSize){
9444             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9445         },
9446
9447         /**
9448          * Returns the width and height of the viewport.
9449          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9450          */
9451         getViewSize : function(){
9452             var d = this.dom, doc = document, aw = 0, ah = 0;
9453             if(d == doc || d == doc.body){
9454                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9455             }else{
9456                 return {
9457                     width : d.clientWidth,
9458                     height: d.clientHeight
9459                 };
9460             }
9461         },
9462
9463         /**
9464          * Returns the value of the "value" attribute
9465          * @param {Boolean} asNumber true to parse the value as a number
9466          * @return {String/Number}
9467          */
9468         getValue : function(asNumber){
9469             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9470         },
9471
9472         // private
9473         adjustWidth : function(width){
9474             if(typeof width == "number"){
9475                 if(this.autoBoxAdjust && !this.isBorderBox()){
9476                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9477                 }
9478                 if(width < 0){
9479                     width = 0;
9480                 }
9481             }
9482             return width;
9483         },
9484
9485         // private
9486         adjustHeight : function(height){
9487             if(typeof height == "number"){
9488                if(this.autoBoxAdjust && !this.isBorderBox()){
9489                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9490                }
9491                if(height < 0){
9492                    height = 0;
9493                }
9494             }
9495             return height;
9496         },
9497
9498         /**
9499          * Set the width of the element
9500          * @param {Number} width The new width
9501          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9502          * @return {Roo.Element} this
9503          */
9504         setWidth : function(width, animate){
9505             width = this.adjustWidth(width);
9506             if(!animate || !A){
9507                 this.dom.style.width = this.addUnits(width);
9508             }else{
9509                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9510             }
9511             return this;
9512         },
9513
9514         /**
9515          * Set the height of the element
9516          * @param {Number} height The new height
9517          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9518          * @return {Roo.Element} this
9519          */
9520          setHeight : function(height, animate){
9521             height = this.adjustHeight(height);
9522             if(!animate || !A){
9523                 this.dom.style.height = this.addUnits(height);
9524             }else{
9525                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9526             }
9527             return this;
9528         },
9529
9530         /**
9531          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9532          * @param {Number} width The new width
9533          * @param {Number} height The new height
9534          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9535          * @return {Roo.Element} this
9536          */
9537          setSize : function(width, height, animate){
9538             if(typeof width == "object"){ // in case of object from getSize()
9539                 height = width.height; width = width.width;
9540             }
9541             width = this.adjustWidth(width); height = this.adjustHeight(height);
9542             if(!animate || !A){
9543                 this.dom.style.width = this.addUnits(width);
9544                 this.dom.style.height = this.addUnits(height);
9545             }else{
9546                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9547             }
9548             return this;
9549         },
9550
9551         /**
9552          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9553          * @param {Number} x X value for new position (coordinates are page-based)
9554          * @param {Number} y Y value for new position (coordinates are page-based)
9555          * @param {Number} width The new width
9556          * @param {Number} height The new height
9557          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9558          * @return {Roo.Element} this
9559          */
9560         setBounds : function(x, y, width, height, animate){
9561             if(!animate || !A){
9562                 this.setSize(width, height);
9563                 this.setLocation(x, y);
9564             }else{
9565                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9566                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9567                               this.preanim(arguments, 4), 'motion');
9568             }
9569             return this;
9570         },
9571
9572         /**
9573          * 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.
9574          * @param {Roo.lib.Region} region The region to fill
9575          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9576          * @return {Roo.Element} this
9577          */
9578         setRegion : function(region, animate){
9579             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9580             return this;
9581         },
9582
9583         /**
9584          * Appends an event handler
9585          *
9586          * @param {String}   eventName     The type of event to append
9587          * @param {Function} fn        The method the event invokes
9588          * @param {Object} scope       (optional) The scope (this object) of the fn
9589          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9590          */
9591         addListener : function(eventName, fn, scope, options)
9592         {
9593             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9594                 this.addListener('touchstart', this.onTapHandler, this);
9595             }
9596             
9597             // we need to handle a special case where dom element is a svg element.
9598             // in this case we do not actua
9599             if (!this.dom) {
9600                 return;
9601             }
9602             
9603             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9604                 if (typeof(this.listeners[eventName]) == 'undefined') {
9605                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9606                 }
9607                 this.listeners[eventName].addListener(fn, scope, options);
9608                 return;
9609             }
9610             
9611                 
9612             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9613             
9614             
9615         },
9616         tapedTwice : false,
9617         onTapHandler : function(event)
9618         {
9619             if(!this.tapedTwice) {
9620                 this.tapedTwice = true;
9621                 var s = this;
9622                 setTimeout( function() {
9623                     s.tapedTwice = false;
9624                 }, 300 );
9625                 return;
9626             }
9627             event.preventDefault();
9628             var revent = new MouseEvent('dblclick',  {
9629                 view: window,
9630                 bubbles: true,
9631                 cancelable: true
9632             });
9633              
9634             this.dom.dispatchEvent(revent);
9635             //action on double tap goes below
9636              
9637         }, 
9638  
9639         /**
9640          * Removes an event handler from this element
9641          * @param {String} eventName the type of event to remove
9642          * @param {Function} fn the method the event invokes
9643          * @param {Function} scope (needed for svg fake listeners)
9644          * @return {Roo.Element} this
9645          */
9646         removeListener : function(eventName, fn, scope){
9647             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9648             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9649                 return this;
9650             }
9651             this.listeners[eventName].removeListener(fn, scope);
9652             return this;
9653         },
9654
9655         /**
9656          * Removes all previous added listeners from this element
9657          * @return {Roo.Element} this
9658          */
9659         removeAllListeners : function(){
9660             E.purgeElement(this.dom);
9661             this.listeners = {};
9662             return this;
9663         },
9664
9665         relayEvent : function(eventName, observable){
9666             this.on(eventName, function(e){
9667                 observable.fireEvent(eventName, e);
9668             });
9669         },
9670
9671         
9672         /**
9673          * Set the opacity of the element
9674          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9676          * @return {Roo.Element} this
9677          */
9678          setOpacity : function(opacity, animate){
9679             if(!animate || !A){
9680                 var s = this.dom.style;
9681                 if(Roo.isIE){
9682                     s.zoom = 1;
9683                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9684                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9685                 }else{
9686                     s.opacity = opacity;
9687                 }
9688             }else{
9689                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9690             }
9691             return this;
9692         },
9693
9694         /**
9695          * Gets the left X coordinate
9696          * @param {Boolean} local True to get the local css position instead of page coordinate
9697          * @return {Number}
9698          */
9699         getLeft : function(local){
9700             if(!local){
9701                 return this.getX();
9702             }else{
9703                 return parseInt(this.getStyle("left"), 10) || 0;
9704             }
9705         },
9706
9707         /**
9708          * Gets the right X coordinate of the element (element X position + element width)
9709          * @param {Boolean} local True to get the local css position instead of page coordinate
9710          * @return {Number}
9711          */
9712         getRight : function(local){
9713             if(!local){
9714                 return this.getX() + this.getWidth();
9715             }else{
9716                 return (this.getLeft(true) + this.getWidth()) || 0;
9717             }
9718         },
9719
9720         /**
9721          * Gets the top Y coordinate
9722          * @param {Boolean} local True to get the local css position instead of page coordinate
9723          * @return {Number}
9724          */
9725         getTop : function(local) {
9726             if(!local){
9727                 return this.getY();
9728             }else{
9729                 return parseInt(this.getStyle("top"), 10) || 0;
9730             }
9731         },
9732
9733         /**
9734          * Gets the bottom Y coordinate of the element (element Y position + element height)
9735          * @param {Boolean} local True to get the local css position instead of page coordinate
9736          * @return {Number}
9737          */
9738         getBottom : function(local){
9739             if(!local){
9740                 return this.getY() + this.getHeight();
9741             }else{
9742                 return (this.getTop(true) + this.getHeight()) || 0;
9743             }
9744         },
9745
9746         /**
9747         * Initializes positioning on this element. If a desired position is not passed, it will make the
9748         * the element positioned relative IF it is not already positioned.
9749         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9750         * @param {Number} zIndex (optional) The zIndex to apply
9751         * @param {Number} x (optional) Set the page X position
9752         * @param {Number} y (optional) Set the page Y position
9753         */
9754         position : function(pos, zIndex, x, y){
9755             if(!pos){
9756                if(this.getStyle('position') == 'static'){
9757                    this.setStyle('position', 'relative');
9758                }
9759             }else{
9760                 this.setStyle("position", pos);
9761             }
9762             if(zIndex){
9763                 this.setStyle("z-index", zIndex);
9764             }
9765             if(x !== undefined && y !== undefined){
9766                 this.setXY([x, y]);
9767             }else if(x !== undefined){
9768                 this.setX(x);
9769             }else if(y !== undefined){
9770                 this.setY(y);
9771             }
9772         },
9773
9774         /**
9775         * Clear positioning back to the default when the document was loaded
9776         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9777         * @return {Roo.Element} this
9778          */
9779         clearPositioning : function(value){
9780             value = value ||'';
9781             this.setStyle({
9782                 "left": value,
9783                 "right": value,
9784                 "top": value,
9785                 "bottom": value,
9786                 "z-index": "",
9787                 "position" : "static"
9788             });
9789             return this;
9790         },
9791
9792         /**
9793         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9794         * snapshot before performing an update and then restoring the element.
9795         * @return {Object}
9796         */
9797         getPositioning : function(){
9798             var l = this.getStyle("left");
9799             var t = this.getStyle("top");
9800             return {
9801                 "position" : this.getStyle("position"),
9802                 "left" : l,
9803                 "right" : l ? "" : this.getStyle("right"),
9804                 "top" : t,
9805                 "bottom" : t ? "" : this.getStyle("bottom"),
9806                 "z-index" : this.getStyle("z-index")
9807             };
9808         },
9809
9810         /**
9811          * Gets the width of the border(s) for the specified side(s)
9812          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9813          * passing lr would get the border (l)eft width + the border (r)ight width.
9814          * @return {Number} The width of the sides passed added together
9815          */
9816         getBorderWidth : function(side){
9817             return this.addStyles(side, El.borders);
9818         },
9819
9820         /**
9821          * Gets the width of the padding(s) for the specified side(s)
9822          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9823          * passing lr would get the padding (l)eft + the padding (r)ight.
9824          * @return {Number} The padding of the sides passed added together
9825          */
9826         getPadding : function(side){
9827             return this.addStyles(side, El.paddings);
9828         },
9829
9830         /**
9831         * Set positioning with an object returned by getPositioning().
9832         * @param {Object} posCfg
9833         * @return {Roo.Element} this
9834          */
9835         setPositioning : function(pc){
9836             this.applyStyles(pc);
9837             if(pc.right == "auto"){
9838                 this.dom.style.right = "";
9839             }
9840             if(pc.bottom == "auto"){
9841                 this.dom.style.bottom = "";
9842             }
9843             return this;
9844         },
9845
9846         // private
9847         fixDisplay : function(){
9848             if(this.getStyle("display") == "none"){
9849                 this.setStyle("visibility", "hidden");
9850                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9851                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9852                     this.setStyle("display", "block");
9853                 }
9854             }
9855         },
9856
9857         /**
9858          * Quick set left and top adding default units
9859          * @param {String} left The left CSS property value
9860          * @param {String} top The top CSS property value
9861          * @return {Roo.Element} this
9862          */
9863          setLeftTop : function(left, top){
9864             this.dom.style.left = this.addUnits(left);
9865             this.dom.style.top = this.addUnits(top);
9866             return this;
9867         },
9868
9869         /**
9870          * Move this element relative to its current position.
9871          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9872          * @param {Number} distance How far to move the element in pixels
9873          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9874          * @return {Roo.Element} this
9875          */
9876          move : function(direction, distance, animate){
9877             var xy = this.getXY();
9878             direction = direction.toLowerCase();
9879             switch(direction){
9880                 case "l":
9881                 case "left":
9882                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9883                     break;
9884                case "r":
9885                case "right":
9886                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9887                     break;
9888                case "t":
9889                case "top":
9890                case "up":
9891                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9892                     break;
9893                case "b":
9894                case "bottom":
9895                case "down":
9896                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9897                     break;
9898             }
9899             return this;
9900         },
9901
9902         /**
9903          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9904          * @return {Roo.Element} this
9905          */
9906         clip : function(){
9907             if(!this.isClipped){
9908                this.isClipped = true;
9909                this.originalClip = {
9910                    "o": this.getStyle("overflow"),
9911                    "x": this.getStyle("overflow-x"),
9912                    "y": this.getStyle("overflow-y")
9913                };
9914                this.setStyle("overflow", "hidden");
9915                this.setStyle("overflow-x", "hidden");
9916                this.setStyle("overflow-y", "hidden");
9917             }
9918             return this;
9919         },
9920
9921         /**
9922          *  Return clipping (overflow) to original clipping before clip() was called
9923          * @return {Roo.Element} this
9924          */
9925         unclip : function(){
9926             if(this.isClipped){
9927                 this.isClipped = false;
9928                 var o = this.originalClip;
9929                 if(o.o){this.setStyle("overflow", o.o);}
9930                 if(o.x){this.setStyle("overflow-x", o.x);}
9931                 if(o.y){this.setStyle("overflow-y", o.y);}
9932             }
9933             return this;
9934         },
9935
9936
9937         /**
9938          * Gets the x,y coordinates specified by the anchor position on the element.
9939          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9940          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9941          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9942          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9943          * @return {Array} [x, y] An array containing the element's x and y coordinates
9944          */
9945         getAnchorXY : function(anchor, local, s){
9946             //Passing a different size is useful for pre-calculating anchors,
9947             //especially for anchored animations that change the el size.
9948
9949             var w, h, vp = false;
9950             if(!s){
9951                 var d = this.dom;
9952                 if(d == document.body || d == document){
9953                     vp = true;
9954                     w = D.getViewWidth(); h = D.getViewHeight();
9955                 }else{
9956                     w = this.getWidth(); h = this.getHeight();
9957                 }
9958             }else{
9959                 w = s.width;  h = s.height;
9960             }
9961             var x = 0, y = 0, r = Math.round;
9962             switch((anchor || "tl").toLowerCase()){
9963                 case "c":
9964                     x = r(w*.5);
9965                     y = r(h*.5);
9966                 break;
9967                 case "t":
9968                     x = r(w*.5);
9969                     y = 0;
9970                 break;
9971                 case "l":
9972                     x = 0;
9973                     y = r(h*.5);
9974                 break;
9975                 case "r":
9976                     x = w;
9977                     y = r(h*.5);
9978                 break;
9979                 case "b":
9980                     x = r(w*.5);
9981                     y = h;
9982                 break;
9983                 case "tl":
9984                     x = 0;
9985                     y = 0;
9986                 break;
9987                 case "bl":
9988                     x = 0;
9989                     y = h;
9990                 break;
9991                 case "br":
9992                     x = w;
9993                     y = h;
9994                 break;
9995                 case "tr":
9996                     x = w;
9997                     y = 0;
9998                 break;
9999             }
10000             if(local === true){
10001                 return [x, y];
10002             }
10003             if(vp){
10004                 var sc = this.getScroll();
10005                 return [x + sc.left, y + sc.top];
10006             }
10007             //Add the element's offset xy
10008             var o = this.getXY();
10009             return [x+o[0], y+o[1]];
10010         },
10011
10012         /**
10013          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10014          * supported position values.
10015          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10016          * @param {String} position The position to align to.
10017          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10018          * @return {Array} [x, y]
10019          */
10020         getAlignToXY : function(el, p, o)
10021         {
10022             el = Roo.get(el);
10023             var d = this.dom;
10024             if(!el.dom){
10025                 throw "Element.alignTo with an element that doesn't exist";
10026             }
10027             var c = false; //constrain to viewport
10028             var p1 = "", p2 = "";
10029             o = o || [0,0];
10030
10031             if(!p){
10032                 p = "tl-bl";
10033             }else if(p == "?"){
10034                 p = "tl-bl?";
10035             }else if(p.indexOf("-") == -1){
10036                 p = "tl-" + p;
10037             }
10038             p = p.toLowerCase();
10039             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10040             if(!m){
10041                throw "Element.alignTo with an invalid alignment " + p;
10042             }
10043             p1 = m[1]; p2 = m[2]; c = !!m[3];
10044
10045             //Subtract the aligned el's internal xy from the target's offset xy
10046             //plus custom offset to get the aligned el's new offset xy
10047             var a1 = this.getAnchorXY(p1, true);
10048             var a2 = el.getAnchorXY(p2, false);
10049             var x = a2[0] - a1[0] + o[0];
10050             var y = a2[1] - a1[1] + o[1];
10051             if(c){
10052                 //constrain the aligned el to viewport if necessary
10053                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10054                 // 5px of margin for ie
10055                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10056
10057                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10058                 //perpendicular to the vp border, allow the aligned el to slide on that border,
10059                 //otherwise swap the aligned el to the opposite border of the target.
10060                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10061                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10062                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
10063                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10064
10065                var doc = document;
10066                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10067                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10068
10069                if((x+w) > dw + scrollX){
10070                     x = swapX ? r.left-w : dw+scrollX-w;
10071                 }
10072                if(x < scrollX){
10073                    x = swapX ? r.right : scrollX;
10074                }
10075                if((y+h) > dh + scrollY){
10076                     y = swapY ? r.top-h : dh+scrollY-h;
10077                 }
10078                if (y < scrollY){
10079                    y = swapY ? r.bottom : scrollY;
10080                }
10081             }
10082             return [x,y];
10083         },
10084
10085         // private
10086         getConstrainToXY : function(){
10087             var os = {top:0, left:0, bottom:0, right: 0};
10088
10089             return function(el, local, offsets, proposedXY){
10090                 el = Roo.get(el);
10091                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10092
10093                 var vw, vh, vx = 0, vy = 0;
10094                 if(el.dom == document.body || el.dom == document){
10095                     vw = Roo.lib.Dom.getViewWidth();
10096                     vh = Roo.lib.Dom.getViewHeight();
10097                 }else{
10098                     vw = el.dom.clientWidth;
10099                     vh = el.dom.clientHeight;
10100                     if(!local){
10101                         var vxy = el.getXY();
10102                         vx = vxy[0];
10103                         vy = vxy[1];
10104                     }
10105                 }
10106
10107                 var s = el.getScroll();
10108
10109                 vx += offsets.left + s.left;
10110                 vy += offsets.top + s.top;
10111
10112                 vw -= offsets.right;
10113                 vh -= offsets.bottom;
10114
10115                 var vr = vx+vw;
10116                 var vb = vy+vh;
10117
10118                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10119                 var x = xy[0], y = xy[1];
10120                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10121
10122                 // only move it if it needs it
10123                 var moved = false;
10124
10125                 // first validate right/bottom
10126                 if((x + w) > vr){
10127                     x = vr - w;
10128                     moved = true;
10129                 }
10130                 if((y + h) > vb){
10131                     y = vb - h;
10132                     moved = true;
10133                 }
10134                 // then make sure top/left isn't negative
10135                 if(x < vx){
10136                     x = vx;
10137                     moved = true;
10138                 }
10139                 if(y < vy){
10140                     y = vy;
10141                     moved = true;
10142                 }
10143                 return moved ? [x, y] : false;
10144             };
10145         }(),
10146
10147         // private
10148         adjustForConstraints : function(xy, parent, offsets){
10149             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
10150         },
10151
10152         /**
10153          * Aligns this element with another element relative to the specified anchor points. If the other element is the
10154          * document it aligns it to the viewport.
10155          * The position parameter is optional, and can be specified in any one of the following formats:
10156          * <ul>
10157          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10158          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10159          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
10160          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
10161          *   <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
10162          *       element's anchor point, and the second value is used as the target's anchor point.</li>
10163          * </ul>
10164          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
10165          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10166          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
10167          * that specified in order to enforce the viewport constraints.
10168          * Following are all of the supported anchor positions:
10169     <pre>
10170     Value  Description
10171     -----  -----------------------------
10172     tl     The top left corner (default)
10173     t      The center of the top edge
10174     tr     The top right corner
10175     l      The center of the left edge
10176     c      In the center of the element
10177     r      The center of the right edge
10178     bl     The bottom left corner
10179     b      The center of the bottom edge
10180     br     The bottom right corner
10181     </pre>
10182     Example Usage:
10183     <pre><code>
10184     // align el to other-el using the default positioning ("tl-bl", non-constrained)
10185     el.alignTo("other-el");
10186
10187     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10188     el.alignTo("other-el", "tr?");
10189
10190     // align the bottom right corner of el with the center left edge of other-el
10191     el.alignTo("other-el", "br-l?");
10192
10193     // align the center of el with the bottom left corner of other-el and
10194     // adjust the x position by -6 pixels (and the y position by 0)
10195     el.alignTo("other-el", "c-bl", [-6, 0]);
10196     </code></pre>
10197          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10198          * @param {String} position The position to align to.
10199          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10200          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10201          * @return {Roo.Element} this
10202          */
10203         alignTo : function(element, position, offsets, animate){
10204             var xy = this.getAlignToXY(element, position, offsets);
10205             this.setXY(xy, this.preanim(arguments, 3));
10206             return this;
10207         },
10208
10209         /**
10210          * Anchors an element to another element and realigns it when the window is resized.
10211          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10212          * @param {String} position The position to align to.
10213          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10214          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10215          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10216          * is a number, it is used as the buffer delay (defaults to 50ms).
10217          * @param {Function} callback The function to call after the animation finishes
10218          * @return {Roo.Element} this
10219          */
10220         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10221             var action = function(){
10222                 this.alignTo(el, alignment, offsets, animate);
10223                 Roo.callback(callback, this);
10224             };
10225             Roo.EventManager.onWindowResize(action, this);
10226             var tm = typeof monitorScroll;
10227             if(tm != 'undefined'){
10228                 Roo.EventManager.on(window, 'scroll', action, this,
10229                     {buffer: tm == 'number' ? monitorScroll : 50});
10230             }
10231             action.call(this); // align immediately
10232             return this;
10233         },
10234         /**
10235          * Clears any opacity settings from this element. Required in some cases for IE.
10236          * @return {Roo.Element} this
10237          */
10238         clearOpacity : function(){
10239             if (window.ActiveXObject) {
10240                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10241                     this.dom.style.filter = "";
10242                 }
10243             } else {
10244                 this.dom.style.opacity = "";
10245                 this.dom.style["-moz-opacity"] = "";
10246                 this.dom.style["-khtml-opacity"] = "";
10247             }
10248             return this;
10249         },
10250
10251         /**
10252          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10253          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10254          * @return {Roo.Element} this
10255          */
10256         hide : function(animate){
10257             this.setVisible(false, this.preanim(arguments, 0));
10258             return this;
10259         },
10260
10261         /**
10262         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10263         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10264          * @return {Roo.Element} this
10265          */
10266         show : function(animate){
10267             this.setVisible(true, this.preanim(arguments, 0));
10268             return this;
10269         },
10270
10271         /**
10272          * @private Test if size has a unit, otherwise appends the default
10273          */
10274         addUnits : function(size){
10275             return Roo.Element.addUnits(size, this.defaultUnit);
10276         },
10277
10278         /**
10279          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10280          * @return {Roo.Element} this
10281          */
10282         beginMeasure : function(){
10283             var el = this.dom;
10284             if(el.offsetWidth || el.offsetHeight){
10285                 return this; // offsets work already
10286             }
10287             var changed = [];
10288             var p = this.dom, b = document.body; // start with this element
10289             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10290                 var pe = Roo.get(p);
10291                 if(pe.getStyle('display') == 'none'){
10292                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10293                     p.style.visibility = "hidden";
10294                     p.style.display = "block";
10295                 }
10296                 p = p.parentNode;
10297             }
10298             this._measureChanged = changed;
10299             return this;
10300
10301         },
10302
10303         /**
10304          * Restores displays to before beginMeasure was called
10305          * @return {Roo.Element} this
10306          */
10307         endMeasure : function(){
10308             var changed = this._measureChanged;
10309             if(changed){
10310                 for(var i = 0, len = changed.length; i < len; i++) {
10311                     var r = changed[i];
10312                     r.el.style.visibility = r.visibility;
10313                     r.el.style.display = "none";
10314                 }
10315                 this._measureChanged = null;
10316             }
10317             return this;
10318         },
10319
10320         /**
10321         * Update the innerHTML of this element, optionally searching for and processing scripts
10322         * @param {String} html The new HTML
10323         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10324         * @param {Function} callback For async script loading you can be noticed when the update completes
10325         * @return {Roo.Element} this
10326          */
10327         update : function(html, loadScripts, callback){
10328             if(typeof html == "undefined"){
10329                 html = "";
10330             }
10331             if(loadScripts !== true){
10332                 this.dom.innerHTML = html;
10333                 if(typeof callback == "function"){
10334                     callback();
10335                 }
10336                 return this;
10337             }
10338             var id = Roo.id();
10339             var dom = this.dom;
10340
10341             html += '<span id="' + id + '"></span>';
10342
10343             E.onAvailable(id, function(){
10344                 var hd = document.getElementsByTagName("head")[0];
10345                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10346                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10347                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10348
10349                 var match;
10350                 while(match = re.exec(html)){
10351                     var attrs = match[1];
10352                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10353                     if(srcMatch && srcMatch[2]){
10354                        var s = document.createElement("script");
10355                        s.src = srcMatch[2];
10356                        var typeMatch = attrs.match(typeRe);
10357                        if(typeMatch && typeMatch[2]){
10358                            s.type = typeMatch[2];
10359                        }
10360                        hd.appendChild(s);
10361                     }else if(match[2] && match[2].length > 0){
10362                         if(window.execScript) {
10363                            window.execScript(match[2]);
10364                         } else {
10365                             /**
10366                              * eval:var:id
10367                              * eval:var:dom
10368                              * eval:var:html
10369                              * 
10370                              */
10371                            window.eval(match[2]);
10372                         }
10373                     }
10374                 }
10375                 var el = document.getElementById(id);
10376                 if(el){el.parentNode.removeChild(el);}
10377                 if(typeof callback == "function"){
10378                     callback();
10379                 }
10380             });
10381             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10382             return this;
10383         },
10384
10385         /**
10386          * Direct access to the UpdateManager update() method (takes the same parameters).
10387          * @param {String/Function} url The url for this request or a function to call to get the url
10388          * @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}
10389          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10390          * @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.
10391          * @return {Roo.Element} this
10392          */
10393         load : function(){
10394             var um = this.getUpdateManager();
10395             um.update.apply(um, arguments);
10396             return this;
10397         },
10398
10399         /**
10400         * Gets this element's UpdateManager
10401         * @return {Roo.UpdateManager} The UpdateManager
10402         */
10403         getUpdateManager : function(){
10404             if(!this.updateManager){
10405                 this.updateManager = new Roo.UpdateManager(this);
10406             }
10407             return this.updateManager;
10408         },
10409
10410         /**
10411          * Disables text selection for this element (normalized across browsers)
10412          * @return {Roo.Element} this
10413          */
10414         unselectable : function(){
10415             this.dom.unselectable = "on";
10416             this.swallowEvent("selectstart", true);
10417             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10418             this.addClass("x-unselectable");
10419             return this;
10420         },
10421
10422         /**
10423         * Calculates the x, y to center this element on the screen
10424         * @return {Array} The x, y values [x, y]
10425         */
10426         getCenterXY : function(){
10427             return this.getAlignToXY(document, 'c-c');
10428         },
10429
10430         /**
10431         * Centers the Element in either the viewport, or another Element.
10432         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10433         */
10434         center : function(centerIn){
10435             this.alignTo(centerIn || document, 'c-c');
10436             return this;
10437         },
10438
10439         /**
10440          * Tests various css rules/browsers to determine if this element uses a border box
10441          * @return {Boolean}
10442          */
10443         isBorderBox : function(){
10444             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10445         },
10446
10447         /**
10448          * Return a box {x, y, width, height} that can be used to set another elements
10449          * size/location to match this element.
10450          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10451          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10452          * @return {Object} box An object in the format {x, y, width, height}
10453          */
10454         getBox : function(contentBox, local){
10455             var xy;
10456             if(!local){
10457                 xy = this.getXY();
10458             }else{
10459                 var left = parseInt(this.getStyle("left"), 10) || 0;
10460                 var top = parseInt(this.getStyle("top"), 10) || 0;
10461                 xy = [left, top];
10462             }
10463             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10464             if(!contentBox){
10465                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10466             }else{
10467                 var l = this.getBorderWidth("l")+this.getPadding("l");
10468                 var r = this.getBorderWidth("r")+this.getPadding("r");
10469                 var t = this.getBorderWidth("t")+this.getPadding("t");
10470                 var b = this.getBorderWidth("b")+this.getPadding("b");
10471                 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)};
10472             }
10473             bx.right = bx.x + bx.width;
10474             bx.bottom = bx.y + bx.height;
10475             return bx;
10476         },
10477
10478         /**
10479          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10480          for more information about the sides.
10481          * @param {String} sides
10482          * @return {Number}
10483          */
10484         getFrameWidth : function(sides, onlyContentBox){
10485             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10486         },
10487
10488         /**
10489          * 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.
10490          * @param {Object} box The box to fill {x, y, width, height}
10491          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10492          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10493          * @return {Roo.Element} this
10494          */
10495         setBox : function(box, adjust, animate){
10496             var w = box.width, h = box.height;
10497             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10498                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10499                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10500             }
10501             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10502             return this;
10503         },
10504
10505         /**
10506          * Forces the browser to repaint this element
10507          * @return {Roo.Element} this
10508          */
10509          repaint : function(){
10510             var dom = this.dom;
10511             this.addClass("x-repaint");
10512             setTimeout(function(){
10513                 Roo.get(dom).removeClass("x-repaint");
10514             }, 1);
10515             return this;
10516         },
10517
10518         /**
10519          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10520          * then it returns the calculated width of the sides (see getPadding)
10521          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10522          * @return {Object/Number}
10523          */
10524         getMargins : function(side){
10525             if(!side){
10526                 return {
10527                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10528                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10529                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10530                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10531                 };
10532             }else{
10533                 return this.addStyles(side, El.margins);
10534              }
10535         },
10536
10537         // private
10538         addStyles : function(sides, styles){
10539             var val = 0, v, w;
10540             for(var i = 0, len = sides.length; i < len; i++){
10541                 v = this.getStyle(styles[sides.charAt(i)]);
10542                 if(v){
10543                      w = parseInt(v, 10);
10544                      if(w){ val += w; }
10545                 }
10546             }
10547             return val;
10548         },
10549
10550         /**
10551          * Creates a proxy element of this element
10552          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10553          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10554          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10555          * @return {Roo.Element} The new proxy element
10556          */
10557         createProxy : function(config, renderTo, matchBox){
10558             if(renderTo){
10559                 renderTo = Roo.getDom(renderTo);
10560             }else{
10561                 renderTo = document.body;
10562             }
10563             config = typeof config == "object" ?
10564                 config : {tag : "div", cls: config};
10565             var proxy = Roo.DomHelper.append(renderTo, config, true);
10566             if(matchBox){
10567                proxy.setBox(this.getBox());
10568             }
10569             return proxy;
10570         },
10571
10572         /**
10573          * Puts a mask over this element to disable user interaction. Requires core.css.
10574          * This method can only be applied to elements which accept child nodes.
10575          * @param {String} msg (optional) A message to display in the mask
10576          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10577          * @return {Element} The mask  element
10578          */
10579         mask : function(msg, msgCls)
10580         {
10581             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10582                 this.setStyle("position", "relative");
10583             }
10584             if(!this._mask){
10585                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10586             }
10587             
10588             this.addClass("x-masked");
10589             this._mask.setDisplayed(true);
10590             
10591             // we wander
10592             var z = 0;
10593             var dom = this.dom;
10594             while (dom && dom.style) {
10595                 if (!isNaN(parseInt(dom.style.zIndex))) {
10596                     z = Math.max(z, parseInt(dom.style.zIndex));
10597                 }
10598                 dom = dom.parentNode;
10599             }
10600             // if we are masking the body - then it hides everything..
10601             if (this.dom == document.body) {
10602                 z = 1000000;
10603                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10604                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10605             }
10606            
10607             if(typeof msg == 'string'){
10608                 if(!this._maskMsg){
10609                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10610                         cls: "roo-el-mask-msg", 
10611                         cn: [
10612                             {
10613                                 tag: 'i',
10614                                 cls: 'fa fa-spinner fa-spin'
10615                             },
10616                             {
10617                                 tag: 'div'
10618                             }   
10619                         ]
10620                     }, true);
10621                 }
10622                 var mm = this._maskMsg;
10623                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10624                 if (mm.dom.lastChild) { // weird IE issue?
10625                     mm.dom.lastChild.innerHTML = msg;
10626                 }
10627                 mm.setDisplayed(true);
10628                 mm.center(this);
10629                 mm.setStyle('z-index', z + 102);
10630             }
10631             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10632                 this._mask.setHeight(this.getHeight());
10633             }
10634             this._mask.setStyle('z-index', z + 100);
10635             
10636             return this._mask;
10637         },
10638
10639         /**
10640          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10641          * it is cached for reuse.
10642          */
10643         unmask : function(removeEl){
10644             if(this._mask){
10645                 if(removeEl === true){
10646                     this._mask.remove();
10647                     delete this._mask;
10648                     if(this._maskMsg){
10649                         this._maskMsg.remove();
10650                         delete this._maskMsg;
10651                     }
10652                 }else{
10653                     this._mask.setDisplayed(false);
10654                     if(this._maskMsg){
10655                         this._maskMsg.setDisplayed(false);
10656                     }
10657                 }
10658             }
10659             this.removeClass("x-masked");
10660         },
10661
10662         /**
10663          * Returns true if this element is masked
10664          * @return {Boolean}
10665          */
10666         isMasked : function(){
10667             return this._mask && this._mask.isVisible();
10668         },
10669
10670         /**
10671          * Creates an iframe shim for this element to keep selects and other windowed objects from
10672          * showing through.
10673          * @return {Roo.Element} The new shim element
10674          */
10675         createShim : function(){
10676             var el = document.createElement('iframe');
10677             el.frameBorder = 'no';
10678             el.className = 'roo-shim';
10679             if(Roo.isIE && Roo.isSecure){
10680                 el.src = Roo.SSL_SECURE_URL;
10681             }
10682             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10683             shim.autoBoxAdjust = false;
10684             return shim;
10685         },
10686
10687         /**
10688          * Removes this element from the DOM and deletes it from the cache
10689          */
10690         remove : function(){
10691             if(this.dom.parentNode){
10692                 this.dom.parentNode.removeChild(this.dom);
10693             }
10694             delete El.cache[this.dom.id];
10695         },
10696
10697         /**
10698          * Sets up event handlers to add and remove a css class when the mouse is over this element
10699          * @param {String} className
10700          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10701          * mouseout events for children elements
10702          * @return {Roo.Element} this
10703          */
10704         addClassOnOver : function(className, preventFlicker){
10705             this.on("mouseover", function(){
10706                 Roo.fly(this, '_internal').addClass(className);
10707             }, this.dom);
10708             var removeFn = function(e){
10709                 if(preventFlicker !== true || !e.within(this, true)){
10710                     Roo.fly(this, '_internal').removeClass(className);
10711                 }
10712             };
10713             this.on("mouseout", removeFn, this.dom);
10714             return this;
10715         },
10716
10717         /**
10718          * Sets up event handlers to add and remove a css class when this element has the focus
10719          * @param {String} className
10720          * @return {Roo.Element} this
10721          */
10722         addClassOnFocus : function(className){
10723             this.on("focus", function(){
10724                 Roo.fly(this, '_internal').addClass(className);
10725             }, this.dom);
10726             this.on("blur", function(){
10727                 Roo.fly(this, '_internal').removeClass(className);
10728             }, this.dom);
10729             return this;
10730         },
10731         /**
10732          * 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)
10733          * @param {String} className
10734          * @return {Roo.Element} this
10735          */
10736         addClassOnClick : function(className){
10737             var dom = this.dom;
10738             this.on("mousedown", function(){
10739                 Roo.fly(dom, '_internal').addClass(className);
10740                 var d = Roo.get(document);
10741                 var fn = function(){
10742                     Roo.fly(dom, '_internal').removeClass(className);
10743                     d.removeListener("mouseup", fn);
10744                 };
10745                 d.on("mouseup", fn);
10746             });
10747             return this;
10748         },
10749
10750         /**
10751          * Stops the specified event from bubbling and optionally prevents the default action
10752          * @param {String} eventName
10753          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10754          * @return {Roo.Element} this
10755          */
10756         swallowEvent : function(eventName, preventDefault){
10757             var fn = function(e){
10758                 e.stopPropagation();
10759                 if(preventDefault){
10760                     e.preventDefault();
10761                 }
10762             };
10763             if(eventName instanceof Array){
10764                 for(var i = 0, len = eventName.length; i < len; i++){
10765                      this.on(eventName[i], fn);
10766                 }
10767                 return this;
10768             }
10769             this.on(eventName, fn);
10770             return this;
10771         },
10772
10773         /**
10774          * @private
10775          */
10776         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10777
10778         /**
10779          * Sizes this element to its parent element's dimensions performing
10780          * neccessary box adjustments.
10781          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10782          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10783          * @return {Roo.Element} this
10784          */
10785         fitToParent : function(monitorResize, targetParent) {
10786           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10787           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10788           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10789             return this;
10790           }
10791           var p = Roo.get(targetParent || this.dom.parentNode);
10792           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10793           if (monitorResize === true) {
10794             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10795             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10796           }
10797           return this;
10798         },
10799
10800         /**
10801          * Gets the next sibling, skipping text nodes
10802          * @return {HTMLElement} The next sibling or null
10803          */
10804         getNextSibling : function(){
10805             var n = this.dom.nextSibling;
10806             while(n && n.nodeType != 1){
10807                 n = n.nextSibling;
10808             }
10809             return n;
10810         },
10811
10812         /**
10813          * Gets the previous sibling, skipping text nodes
10814          * @return {HTMLElement} The previous sibling or null
10815          */
10816         getPrevSibling : function(){
10817             var n = this.dom.previousSibling;
10818             while(n && n.nodeType != 1){
10819                 n = n.previousSibling;
10820             }
10821             return n;
10822         },
10823
10824
10825         /**
10826          * Appends the passed element(s) to this element
10827          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10828          * @return {Roo.Element} this
10829          */
10830         appendChild: function(el){
10831             el = Roo.get(el);
10832             el.appendTo(this);
10833             return this;
10834         },
10835
10836         /**
10837          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10838          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10839          * automatically generated with the specified attributes.
10840          * @param {HTMLElement} insertBefore (optional) a child element of this element
10841          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10842          * @return {Roo.Element} The new child element
10843          */
10844         createChild: function(config, insertBefore, returnDom){
10845             config = config || {tag:'div'};
10846             if(insertBefore){
10847                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10848             }
10849             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10850         },
10851
10852         /**
10853          * Appends this element to the passed element
10854          * @param {String/HTMLElement/Element} el The new parent element
10855          * @return {Roo.Element} this
10856          */
10857         appendTo: function(el){
10858             el = Roo.getDom(el);
10859             el.appendChild(this.dom);
10860             return this;
10861         },
10862
10863         /**
10864          * Inserts this element before the passed element in the DOM
10865          * @param {String/HTMLElement/Element} el The element to insert before
10866          * @return {Roo.Element} this
10867          */
10868         insertBefore: function(el){
10869             el = Roo.getDom(el);
10870             el.parentNode.insertBefore(this.dom, el);
10871             return this;
10872         },
10873
10874         /**
10875          * Inserts this element after the passed element in the DOM
10876          * @param {String/HTMLElement/Element} el The element to insert after
10877          * @return {Roo.Element} this
10878          */
10879         insertAfter: function(el){
10880             el = Roo.getDom(el);
10881             el.parentNode.insertBefore(this.dom, el.nextSibling);
10882             return this;
10883         },
10884
10885         /**
10886          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10887          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10888          * @return {Roo.Element} The new child
10889          */
10890         insertFirst: function(el, returnDom){
10891             el = el || {};
10892             if(typeof el == 'object' && !el.nodeType){ // dh config
10893                 return this.createChild(el, this.dom.firstChild, returnDom);
10894             }else{
10895                 el = Roo.getDom(el);
10896                 this.dom.insertBefore(el, this.dom.firstChild);
10897                 return !returnDom ? Roo.get(el) : el;
10898             }
10899         },
10900
10901         /**
10902          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10903          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10904          * @param {String} where (optional) 'before' or 'after' defaults to before
10905          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10906          * @return {Roo.Element} the inserted Element
10907          */
10908         insertSibling: function(el, where, returnDom){
10909             where = where ? where.toLowerCase() : 'before';
10910             el = el || {};
10911             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10912
10913             if(typeof el == 'object' && !el.nodeType){ // dh config
10914                 if(where == 'after' && !this.dom.nextSibling){
10915                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10916                 }else{
10917                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10918                 }
10919
10920             }else{
10921                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10922                             where == 'before' ? this.dom : this.dom.nextSibling);
10923                 if(!returnDom){
10924                     rt = Roo.get(rt);
10925                 }
10926             }
10927             return rt;
10928         },
10929
10930         /**
10931          * Creates and wraps this element with another element
10932          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10933          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10934          * @return {HTMLElement/Element} The newly created wrapper element
10935          */
10936         wrap: function(config, returnDom){
10937             if(!config){
10938                 config = {tag: "div"};
10939             }
10940             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10941             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10942             return newEl;
10943         },
10944
10945         /**
10946          * Replaces the passed element with this element
10947          * @param {String/HTMLElement/Element} el The element to replace
10948          * @return {Roo.Element} this
10949          */
10950         replace: function(el){
10951             el = Roo.get(el);
10952             this.insertBefore(el);
10953             el.remove();
10954             return this;
10955         },
10956
10957         /**
10958          * Inserts an html fragment into this element
10959          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10960          * @param {String} html The HTML fragment
10961          * @param {Boolean} returnEl True to return an Roo.Element
10962          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10963          */
10964         insertHtml : function(where, html, returnEl){
10965             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10966             return returnEl ? Roo.get(el) : el;
10967         },
10968
10969         /**
10970          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10971          * @param {Object} o The object with the attributes
10972          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10973          * @return {Roo.Element} this
10974          */
10975         set : function(o, useSet){
10976             var el = this.dom;
10977             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10978             for(var attr in o){
10979                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10980                 if(attr=="cls"){
10981                     el.className = o["cls"];
10982                 }else{
10983                     if(useSet) {
10984                         el.setAttribute(attr, o[attr]);
10985                     } else {
10986                         el[attr] = o[attr];
10987                     }
10988                 }
10989             }
10990             if(o.style){
10991                 Roo.DomHelper.applyStyles(el, o.style);
10992             }
10993             return this;
10994         },
10995
10996         /**
10997          * Convenience method for constructing a KeyMap
10998          * @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:
10999          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11000          * @param {Function} fn The function to call
11001          * @param {Object} scope (optional) The scope of the function
11002          * @return {Roo.KeyMap} The KeyMap created
11003          */
11004         addKeyListener : function(key, fn, scope){
11005             var config;
11006             if(typeof key != "object" || key instanceof Array){
11007                 config = {
11008                     key: key,
11009                     fn: fn,
11010                     scope: scope
11011                 };
11012             }else{
11013                 config = {
11014                     key : key.key,
11015                     shift : key.shift,
11016                     ctrl : key.ctrl,
11017                     alt : key.alt,
11018                     fn: fn,
11019                     scope: scope
11020                 };
11021             }
11022             return new Roo.KeyMap(this, config);
11023         },
11024
11025         /**
11026          * Creates a KeyMap for this element
11027          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11028          * @return {Roo.KeyMap} The KeyMap created
11029          */
11030         addKeyMap : function(config){
11031             return new Roo.KeyMap(this, config);
11032         },
11033
11034         /**
11035          * Returns true if this element is scrollable.
11036          * @return {Boolean}
11037          */
11038          isScrollable : function(){
11039             var dom = this.dom;
11040             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11041         },
11042
11043         /**
11044          * 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().
11045          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11046          * @param {Number} value The new scroll value
11047          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11048          * @return {Element} this
11049          */
11050
11051         scrollTo : function(side, value, animate){
11052             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11053             if(!animate || !A){
11054                 this.dom[prop] = value;
11055             }else{
11056                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11057                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11058             }
11059             return this;
11060         },
11061
11062         /**
11063          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11064          * within this element's scrollable range.
11065          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11066          * @param {Number} distance How far to scroll the element in pixels
11067          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11068          * @return {Boolean} Returns true if a scroll was triggered or false if the element
11069          * was scrolled as far as it could go.
11070          */
11071          scroll : function(direction, distance, animate){
11072              if(!this.isScrollable()){
11073                  return;
11074              }
11075              var el = this.dom;
11076              var l = el.scrollLeft, t = el.scrollTop;
11077              var w = el.scrollWidth, h = el.scrollHeight;
11078              var cw = el.clientWidth, ch = el.clientHeight;
11079              direction = direction.toLowerCase();
11080              var scrolled = false;
11081              var a = this.preanim(arguments, 2);
11082              switch(direction){
11083                  case "l":
11084                  case "left":
11085                      if(w - l > cw){
11086                          var v = Math.min(l + distance, w-cw);
11087                          this.scrollTo("left", v, a);
11088                          scrolled = true;
11089                      }
11090                      break;
11091                 case "r":
11092                 case "right":
11093                      if(l > 0){
11094                          var v = Math.max(l - distance, 0);
11095                          this.scrollTo("left", v, a);
11096                          scrolled = true;
11097                      }
11098                      break;
11099                 case "t":
11100                 case "top":
11101                 case "up":
11102                      if(t > 0){
11103                          var v = Math.max(t - distance, 0);
11104                          this.scrollTo("top", v, a);
11105                          scrolled = true;
11106                      }
11107                      break;
11108                 case "b":
11109                 case "bottom":
11110                 case "down":
11111                      if(h - t > ch){
11112                          var v = Math.min(t + distance, h-ch);
11113                          this.scrollTo("top", v, a);
11114                          scrolled = true;
11115                      }
11116                      break;
11117              }
11118              return scrolled;
11119         },
11120
11121         /**
11122          * Translates the passed page coordinates into left/top css values for this element
11123          * @param {Number/Array} x The page x or an array containing [x, y]
11124          * @param {Number} y The page y
11125          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11126          */
11127         translatePoints : function(x, y){
11128             if(typeof x == 'object' || x instanceof Array){
11129                 y = x[1]; x = x[0];
11130             }
11131             var p = this.getStyle('position');
11132             var o = this.getXY();
11133
11134             var l = parseInt(this.getStyle('left'), 10);
11135             var t = parseInt(this.getStyle('top'), 10);
11136
11137             if(isNaN(l)){
11138                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11139             }
11140             if(isNaN(t)){
11141                 t = (p == "relative") ? 0 : this.dom.offsetTop;
11142             }
11143
11144             return {left: (x - o[0] + l), top: (y - o[1] + t)};
11145         },
11146
11147         /**
11148          * Returns the current scroll position of the element.
11149          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11150          */
11151         getScroll : function(){
11152             var d = this.dom, doc = document;
11153             if(d == doc || d == doc.body){
11154                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11155                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11156                 return {left: l, top: t};
11157             }else{
11158                 return {left: d.scrollLeft, top: d.scrollTop};
11159             }
11160         },
11161
11162         /**
11163          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11164          * are convert to standard 6 digit hex color.
11165          * @param {String} attr The css attribute
11166          * @param {String} defaultValue The default value to use when a valid color isn't found
11167          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11168          * YUI color anims.
11169          */
11170         getColor : function(attr, defaultValue, prefix){
11171             var v = this.getStyle(attr);
11172             if(!v || v == "transparent" || v == "inherit") {
11173                 return defaultValue;
11174             }
11175             var color = typeof prefix == "undefined" ? "#" : prefix;
11176             if(v.substr(0, 4) == "rgb("){
11177                 var rvs = v.slice(4, v.length -1).split(",");
11178                 for(var i = 0; i < 3; i++){
11179                     var h = parseInt(rvs[i]).toString(16);
11180                     if(h < 16){
11181                         h = "0" + h;
11182                     }
11183                     color += h;
11184                 }
11185             } else {
11186                 if(v.substr(0, 1) == "#"){
11187                     if(v.length == 4) {
11188                         for(var i = 1; i < 4; i++){
11189                             var c = v.charAt(i);
11190                             color +=  c + c;
11191                         }
11192                     }else if(v.length == 7){
11193                         color += v.substr(1);
11194                     }
11195                 }
11196             }
11197             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11198         },
11199
11200         /**
11201          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11202          * gradient background, rounded corners and a 4-way shadow.
11203          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11204          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11205          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11206          * @return {Roo.Element} this
11207          */
11208         boxWrap : function(cls){
11209             cls = cls || 'x-box';
11210             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11211             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11212             return el;
11213         },
11214
11215         /**
11216          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11217          * @param {String} namespace The namespace in which to look for the attribute
11218          * @param {String} name The attribute name
11219          * @return {String} The attribute value
11220          */
11221         getAttributeNS : Roo.isIE ? function(ns, name){
11222             var d = this.dom;
11223             var type = typeof d[ns+":"+name];
11224             if(type != 'undefined' && type != 'unknown'){
11225                 return d[ns+":"+name];
11226             }
11227             return d[name];
11228         } : function(ns, name){
11229             var d = this.dom;
11230             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11231         },
11232         
11233         
11234         /**
11235          * Sets or Returns the value the dom attribute value
11236          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11237          * @param {String} value (optional) The value to set the attribute to
11238          * @return {String} The attribute value
11239          */
11240         attr : function(name){
11241             if (arguments.length > 1) {
11242                 this.dom.setAttribute(name, arguments[1]);
11243                 return arguments[1];
11244             }
11245             if (typeof(name) == 'object') {
11246                 for(var i in name) {
11247                     this.attr(i, name[i]);
11248                 }
11249                 return name;
11250             }
11251             
11252             
11253             if (!this.dom.hasAttribute(name)) {
11254                 return undefined;
11255             }
11256             return this.dom.getAttribute(name);
11257         }
11258         
11259         
11260         
11261     };
11262
11263     var ep = El.prototype;
11264
11265     /**
11266      * Appends an event handler (Shorthand for addListener)
11267      * @param {String}   eventName     The type of event to append
11268      * @param {Function} fn        The method the event invokes
11269      * @param {Object} scope       (optional) The scope (this object) of the fn
11270      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11271      * @method
11272      */
11273     ep.on = ep.addListener;
11274         // backwards compat
11275     ep.mon = ep.addListener;
11276
11277     /**
11278      * Removes an event handler from this element (shorthand for removeListener)
11279      * @param {String} eventName the type of event to remove
11280      * @param {Function} fn the method the event invokes
11281      * @return {Roo.Element} this
11282      * @method
11283      */
11284     ep.un = ep.removeListener;
11285
11286     /**
11287      * true to automatically adjust width and height settings for box-model issues (default to true)
11288      */
11289     ep.autoBoxAdjust = true;
11290
11291     // private
11292     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11293
11294     // private
11295     El.addUnits = function(v, defaultUnit){
11296         if(v === "" || v == "auto"){
11297             return v;
11298         }
11299         if(v === undefined){
11300             return '';
11301         }
11302         if(typeof v == "number" || !El.unitPattern.test(v)){
11303             return v + (defaultUnit || 'px');
11304         }
11305         return v;
11306     };
11307
11308     // special markup used throughout Roo when box wrapping elements
11309     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>';
11310     /**
11311      * Visibility mode constant - Use visibility to hide element
11312      * @static
11313      * @type Number
11314      */
11315     El.VISIBILITY = 1;
11316     /**
11317      * Visibility mode constant - Use display to hide element
11318      * @static
11319      * @type Number
11320      */
11321     El.DISPLAY = 2;
11322
11323     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11324     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11325     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11326
11327
11328
11329     /**
11330      * @private
11331      */
11332     El.cache = {};
11333
11334     var docEl;
11335
11336     /**
11337      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11338      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11339      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11340      * @return {Element} The Element object
11341      * @static
11342      */
11343     El.get = function(el){
11344         var ex, elm, id;
11345         if(!el){ return null; }
11346         if(typeof el == "string"){ // element id
11347             if(!(elm = document.getElementById(el))){
11348                 return null;
11349             }
11350             if(ex = El.cache[el]){
11351                 ex.dom = elm;
11352             }else{
11353                 ex = El.cache[el] = new El(elm);
11354             }
11355             return ex;
11356         }else if(el.tagName){ // dom element
11357             if(!(id = el.id)){
11358                 id = Roo.id(el);
11359             }
11360             if(ex = El.cache[id]){
11361                 ex.dom = el;
11362             }else{
11363                 ex = El.cache[id] = new El(el);
11364             }
11365             return ex;
11366         }else if(el instanceof El){
11367             if(el != docEl){
11368                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11369                                                               // catch case where it hasn't been appended
11370                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11371             }
11372             return el;
11373         }else if(el.isComposite){
11374             return el;
11375         }else if(el instanceof Array){
11376             return El.select(el);
11377         }else if(el == document){
11378             // create a bogus element object representing the document object
11379             if(!docEl){
11380                 var f = function(){};
11381                 f.prototype = El.prototype;
11382                 docEl = new f();
11383                 docEl.dom = document;
11384             }
11385             return docEl;
11386         }
11387         return null;
11388     };
11389
11390     // private
11391     El.uncache = function(el){
11392         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11393             if(a[i]){
11394                 delete El.cache[a[i].id || a[i]];
11395             }
11396         }
11397     };
11398
11399     // private
11400     // Garbage collection - uncache elements/purge listeners on orphaned elements
11401     // so we don't hold a reference and cause the browser to retain them
11402     El.garbageCollect = function(){
11403         if(!Roo.enableGarbageCollector){
11404             clearInterval(El.collectorThread);
11405             return;
11406         }
11407         for(var eid in El.cache){
11408             var el = El.cache[eid], d = el.dom;
11409             // -------------------------------------------------------
11410             // Determining what is garbage:
11411             // -------------------------------------------------------
11412             // !d
11413             // dom node is null, definitely garbage
11414             // -------------------------------------------------------
11415             // !d.parentNode
11416             // no parentNode == direct orphan, definitely garbage
11417             // -------------------------------------------------------
11418             // !d.offsetParent && !document.getElementById(eid)
11419             // display none elements have no offsetParent so we will
11420             // also try to look it up by it's id. However, check
11421             // offsetParent first so we don't do unneeded lookups.
11422             // This enables collection of elements that are not orphans
11423             // directly, but somewhere up the line they have an orphan
11424             // parent.
11425             // -------------------------------------------------------
11426             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11427                 delete El.cache[eid];
11428                 if(d && Roo.enableListenerCollection){
11429                     E.purgeElement(d);
11430                 }
11431             }
11432         }
11433     }
11434     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11435
11436
11437     // dom is optional
11438     El.Flyweight = function(dom){
11439         this.dom = dom;
11440     };
11441     El.Flyweight.prototype = El.prototype;
11442
11443     El._flyweights = {};
11444     /**
11445      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11446      * the dom node can be overwritten by other code.
11447      * @param {String/HTMLElement} el The dom node or id
11448      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11449      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11450      * @static
11451      * @return {Element} The shared Element object
11452      */
11453     El.fly = function(el, named){
11454         named = named || '_global';
11455         el = Roo.getDom(el);
11456         if(!el){
11457             return null;
11458         }
11459         if(!El._flyweights[named]){
11460             El._flyweights[named] = new El.Flyweight();
11461         }
11462         El._flyweights[named].dom = el;
11463         return El._flyweights[named];
11464     };
11465
11466     /**
11467      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11468      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11469      * Shorthand of {@link Roo.Element#get}
11470      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11471      * @return {Element} The Element object
11472      * @member Roo
11473      * @method get
11474      */
11475     Roo.get = El.get;
11476     /**
11477      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11478      * the dom node can be overwritten by other code.
11479      * Shorthand of {@link Roo.Element#fly}
11480      * @param {String/HTMLElement} el The dom node or id
11481      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11482      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11483      * @static
11484      * @return {Element} The shared Element object
11485      * @member Roo
11486      * @method fly
11487      */
11488     Roo.fly = El.fly;
11489
11490     // speedy lookup for elements never to box adjust
11491     var noBoxAdjust = Roo.isStrict ? {
11492         select:1
11493     } : {
11494         input:1, select:1, textarea:1
11495     };
11496     if(Roo.isIE || Roo.isGecko){
11497         noBoxAdjust['button'] = 1;
11498     }
11499
11500
11501     Roo.EventManager.on(window, 'unload', function(){
11502         delete El.cache;
11503         delete El._flyweights;
11504     });
11505 })();
11506
11507
11508
11509
11510 if(Roo.DomQuery){
11511     Roo.Element.selectorFunction = Roo.DomQuery.select;
11512 }
11513
11514 Roo.Element.select = function(selector, unique, root){
11515     var els;
11516     if(typeof selector == "string"){
11517         els = Roo.Element.selectorFunction(selector, root);
11518     }else if(selector.length !== undefined){
11519         els = selector;
11520     }else{
11521         throw "Invalid selector";
11522     }
11523     if(unique === true){
11524         return new Roo.CompositeElement(els);
11525     }else{
11526         return new Roo.CompositeElementLite(els);
11527     }
11528 };
11529 /**
11530  * Selects elements based on the passed CSS selector to enable working on them as 1.
11531  * @param {String/Array} selector The CSS selector or an array of elements
11532  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11533  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11534  * @return {CompositeElementLite/CompositeElement}
11535  * @member Roo
11536  * @method select
11537  */
11538 Roo.select = Roo.Element.select;
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553 /*
11554  * Based on:
11555  * Ext JS Library 1.1.1
11556  * Copyright(c) 2006-2007, Ext JS, LLC.
11557  *
11558  * Originally Released Under LGPL - original licence link has changed is not relivant.
11559  *
11560  * Fork - LGPL
11561  * <script type="text/javascript">
11562  */
11563
11564
11565
11566 //Notifies Element that fx methods are available
11567 Roo.enableFx = true;
11568
11569 /**
11570  * @class Roo.Fx
11571  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11572  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11573  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11574  * Element effects to work.</p><br/>
11575  *
11576  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11577  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11578  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11579  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11580  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11581  * expected results and should be done with care.</p><br/>
11582  *
11583  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11584  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11585 <pre>
11586 Value  Description
11587 -----  -----------------------------
11588 tl     The top left corner
11589 t      The center of the top edge
11590 tr     The top right corner
11591 l      The center of the left edge
11592 r      The center of the right edge
11593 bl     The bottom left corner
11594 b      The center of the bottom edge
11595 br     The bottom right corner
11596 </pre>
11597  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11598  * below are common options that can be passed to any Fx method.</b>
11599  * @cfg {Function} callback A function called when the effect is finished
11600  * @cfg {Object} scope The scope of the effect function
11601  * @cfg {String} easing A valid Easing value for the effect
11602  * @cfg {String} afterCls A css class to apply after the effect
11603  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11604  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11605  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11606  * effects that end with the element being visually hidden, ignored otherwise)
11607  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11608  * a function which returns such a specification that will be applied to the Element after the effect finishes
11609  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11610  * @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
11611  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11612  */
11613 Roo.Fx = {
11614         /**
11615          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11616          * origin for the slide effect.  This function automatically handles wrapping the element with
11617          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11618          * Usage:
11619          *<pre><code>
11620 // default: slide the element in from the top
11621 el.slideIn();
11622
11623 // custom: slide the element in from the right with a 2-second duration
11624 el.slideIn('r', { duration: 2 });
11625
11626 // common config options shown with default values
11627 el.slideIn('t', {
11628     easing: 'easeOut',
11629     duration: .5
11630 });
11631 </code></pre>
11632          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11633          * @param {Object} options (optional) Object literal with any of the Fx config options
11634          * @return {Roo.Element} The Element
11635          */
11636     slideIn : function(anchor, o){
11637         var el = this.getFxEl();
11638         o = o || {};
11639
11640         el.queueFx(o, function(){
11641
11642             anchor = anchor || "t";
11643
11644             // fix display to visibility
11645             this.fixDisplay();
11646
11647             // restore values after effect
11648             var r = this.getFxRestore();
11649             var b = this.getBox();
11650             // fixed size for slide
11651             this.setSize(b);
11652
11653             // wrap if needed
11654             var wrap = this.fxWrap(r.pos, o, "hidden");
11655
11656             var st = this.dom.style;
11657             st.visibility = "visible";
11658             st.position = "absolute";
11659
11660             // clear out temp styles after slide and unwrap
11661             var after = function(){
11662                 el.fxUnwrap(wrap, r.pos, o);
11663                 st.width = r.width;
11664                 st.height = r.height;
11665                 el.afterFx(o);
11666             };
11667             // time to calc the positions
11668             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11669
11670             switch(anchor.toLowerCase()){
11671                 case "t":
11672                     wrap.setSize(b.width, 0);
11673                     st.left = st.bottom = "0";
11674                     a = {height: bh};
11675                 break;
11676                 case "l":
11677                     wrap.setSize(0, b.height);
11678                     st.right = st.top = "0";
11679                     a = {width: bw};
11680                 break;
11681                 case "r":
11682                     wrap.setSize(0, b.height);
11683                     wrap.setX(b.right);
11684                     st.left = st.top = "0";
11685                     a = {width: bw, points: pt};
11686                 break;
11687                 case "b":
11688                     wrap.setSize(b.width, 0);
11689                     wrap.setY(b.bottom);
11690                     st.left = st.top = "0";
11691                     a = {height: bh, points: pt};
11692                 break;
11693                 case "tl":
11694                     wrap.setSize(0, 0);
11695                     st.right = st.bottom = "0";
11696                     a = {width: bw, height: bh};
11697                 break;
11698                 case "bl":
11699                     wrap.setSize(0, 0);
11700                     wrap.setY(b.y+b.height);
11701                     st.right = st.top = "0";
11702                     a = {width: bw, height: bh, points: pt};
11703                 break;
11704                 case "br":
11705                     wrap.setSize(0, 0);
11706                     wrap.setXY([b.right, b.bottom]);
11707                     st.left = st.top = "0";
11708                     a = {width: bw, height: bh, points: pt};
11709                 break;
11710                 case "tr":
11711                     wrap.setSize(0, 0);
11712                     wrap.setX(b.x+b.width);
11713                     st.left = st.bottom = "0";
11714                     a = {width: bw, height: bh, points: pt};
11715                 break;
11716             }
11717             this.dom.style.visibility = "visible";
11718             wrap.show();
11719
11720             arguments.callee.anim = wrap.fxanim(a,
11721                 o,
11722                 'motion',
11723                 .5,
11724                 'easeOut', after);
11725         });
11726         return this;
11727     },
11728     
11729         /**
11730          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11731          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11732          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11733          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11734          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11735          * Usage:
11736          *<pre><code>
11737 // default: slide the element out to the top
11738 el.slideOut();
11739
11740 // custom: slide the element out to the right with a 2-second duration
11741 el.slideOut('r', { duration: 2 });
11742
11743 // common config options shown with default values
11744 el.slideOut('t', {
11745     easing: 'easeOut',
11746     duration: .5,
11747     remove: false,
11748     useDisplay: false
11749 });
11750 </code></pre>
11751          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11752          * @param {Object} options (optional) Object literal with any of the Fx config options
11753          * @return {Roo.Element} The Element
11754          */
11755     slideOut : function(anchor, o){
11756         var el = this.getFxEl();
11757         o = o || {};
11758
11759         el.queueFx(o, function(){
11760
11761             anchor = anchor || "t";
11762
11763             // restore values after effect
11764             var r = this.getFxRestore();
11765             
11766             var b = this.getBox();
11767             // fixed size for slide
11768             this.setSize(b);
11769
11770             // wrap if needed
11771             var wrap = this.fxWrap(r.pos, o, "visible");
11772
11773             var st = this.dom.style;
11774             st.visibility = "visible";
11775             st.position = "absolute";
11776
11777             wrap.setSize(b);
11778
11779             var after = function(){
11780                 if(o.useDisplay){
11781                     el.setDisplayed(false);
11782                 }else{
11783                     el.hide();
11784                 }
11785
11786                 el.fxUnwrap(wrap, r.pos, o);
11787
11788                 st.width = r.width;
11789                 st.height = r.height;
11790
11791                 el.afterFx(o);
11792             };
11793
11794             var a, zero = {to: 0};
11795             switch(anchor.toLowerCase()){
11796                 case "t":
11797                     st.left = st.bottom = "0";
11798                     a = {height: zero};
11799                 break;
11800                 case "l":
11801                     st.right = st.top = "0";
11802                     a = {width: zero};
11803                 break;
11804                 case "r":
11805                     st.left = st.top = "0";
11806                     a = {width: zero, points: {to:[b.right, b.y]}};
11807                 break;
11808                 case "b":
11809                     st.left = st.top = "0";
11810                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11811                 break;
11812                 case "tl":
11813                     st.right = st.bottom = "0";
11814                     a = {width: zero, height: zero};
11815                 break;
11816                 case "bl":
11817                     st.right = st.top = "0";
11818                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11819                 break;
11820                 case "br":
11821                     st.left = st.top = "0";
11822                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11823                 break;
11824                 case "tr":
11825                     st.left = st.bottom = "0";
11826                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11827                 break;
11828             }
11829
11830             arguments.callee.anim = wrap.fxanim(a,
11831                 o,
11832                 'motion',
11833                 .5,
11834                 "easeOut", after);
11835         });
11836         return this;
11837     },
11838
11839         /**
11840          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11841          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11842          * The element must be removed from the DOM using the 'remove' config option if desired.
11843          * Usage:
11844          *<pre><code>
11845 // default
11846 el.puff();
11847
11848 // common config options shown with default values
11849 el.puff({
11850     easing: 'easeOut',
11851     duration: .5,
11852     remove: false,
11853     useDisplay: false
11854 });
11855 </code></pre>
11856          * @param {Object} options (optional) Object literal with any of the Fx config options
11857          * @return {Roo.Element} The Element
11858          */
11859     puff : function(o){
11860         var el = this.getFxEl();
11861         o = o || {};
11862
11863         el.queueFx(o, function(){
11864             this.clearOpacity();
11865             this.show();
11866
11867             // restore values after effect
11868             var r = this.getFxRestore();
11869             var st = this.dom.style;
11870
11871             var after = function(){
11872                 if(o.useDisplay){
11873                     el.setDisplayed(false);
11874                 }else{
11875                     el.hide();
11876                 }
11877
11878                 el.clearOpacity();
11879
11880                 el.setPositioning(r.pos);
11881                 st.width = r.width;
11882                 st.height = r.height;
11883                 st.fontSize = '';
11884                 el.afterFx(o);
11885             };
11886
11887             var width = this.getWidth();
11888             var height = this.getHeight();
11889
11890             arguments.callee.anim = this.fxanim({
11891                     width : {to: this.adjustWidth(width * 2)},
11892                     height : {to: this.adjustHeight(height * 2)},
11893                     points : {by: [-(width * .5), -(height * .5)]},
11894                     opacity : {to: 0},
11895                     fontSize: {to:200, unit: "%"}
11896                 },
11897                 o,
11898                 'motion',
11899                 .5,
11900                 "easeOut", after);
11901         });
11902         return this;
11903     },
11904
11905         /**
11906          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11907          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11908          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11909          * Usage:
11910          *<pre><code>
11911 // default
11912 el.switchOff();
11913
11914 // all config options shown with default values
11915 el.switchOff({
11916     easing: 'easeIn',
11917     duration: .3,
11918     remove: false,
11919     useDisplay: false
11920 });
11921 </code></pre>
11922          * @param {Object} options (optional) Object literal with any of the Fx config options
11923          * @return {Roo.Element} The Element
11924          */
11925     switchOff : function(o){
11926         var el = this.getFxEl();
11927         o = o || {};
11928
11929         el.queueFx(o, function(){
11930             this.clearOpacity();
11931             this.clip();
11932
11933             // restore values after effect
11934             var r = this.getFxRestore();
11935             var st = this.dom.style;
11936
11937             var after = function(){
11938                 if(o.useDisplay){
11939                     el.setDisplayed(false);
11940                 }else{
11941                     el.hide();
11942                 }
11943
11944                 el.clearOpacity();
11945                 el.setPositioning(r.pos);
11946                 st.width = r.width;
11947                 st.height = r.height;
11948
11949                 el.afterFx(o);
11950             };
11951
11952             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11953                 this.clearOpacity();
11954                 (function(){
11955                     this.fxanim({
11956                         height:{to:1},
11957                         points:{by:[0, this.getHeight() * .5]}
11958                     }, o, 'motion', 0.3, 'easeIn', after);
11959                 }).defer(100, this);
11960             });
11961         });
11962         return this;
11963     },
11964
11965     /**
11966      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11967      * changed using the "attr" config option) and then fading back to the original color. If no original
11968      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11969      * Usage:
11970 <pre><code>
11971 // default: highlight background to yellow
11972 el.highlight();
11973
11974 // custom: highlight foreground text to blue for 2 seconds
11975 el.highlight("0000ff", { attr: 'color', duration: 2 });
11976
11977 // common config options shown with default values
11978 el.highlight("ffff9c", {
11979     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11980     endColor: (current color) or "ffffff",
11981     easing: 'easeIn',
11982     duration: 1
11983 });
11984 </code></pre>
11985      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11986      * @param {Object} options (optional) Object literal with any of the Fx config options
11987      * @return {Roo.Element} The Element
11988      */ 
11989     highlight : function(color, o){
11990         var el = this.getFxEl();
11991         o = o || {};
11992
11993         el.queueFx(o, function(){
11994             color = color || "ffff9c";
11995             attr = o.attr || "backgroundColor";
11996
11997             this.clearOpacity();
11998             this.show();
11999
12000             var origColor = this.getColor(attr);
12001             var restoreColor = this.dom.style[attr];
12002             endColor = (o.endColor || origColor) || "ffffff";
12003
12004             var after = function(){
12005                 el.dom.style[attr] = restoreColor;
12006                 el.afterFx(o);
12007             };
12008
12009             var a = {};
12010             a[attr] = {from: color, to: endColor};
12011             arguments.callee.anim = this.fxanim(a,
12012                 o,
12013                 'color',
12014                 1,
12015                 'easeIn', after);
12016         });
12017         return this;
12018     },
12019
12020    /**
12021     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12022     * Usage:
12023 <pre><code>
12024 // default: a single light blue ripple
12025 el.frame();
12026
12027 // custom: 3 red ripples lasting 3 seconds total
12028 el.frame("ff0000", 3, { duration: 3 });
12029
12030 // common config options shown with default values
12031 el.frame("C3DAF9", 1, {
12032     duration: 1 //duration of entire animation (not each individual ripple)
12033     // Note: Easing is not configurable and will be ignored if included
12034 });
12035 </code></pre>
12036     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12037     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12038     * @param {Object} options (optional) Object literal with any of the Fx config options
12039     * @return {Roo.Element} The Element
12040     */
12041     frame : function(color, count, o){
12042         var el = this.getFxEl();
12043         o = o || {};
12044
12045         el.queueFx(o, function(){
12046             color = color || "#C3DAF9";
12047             if(color.length == 6){
12048                 color = "#" + color;
12049             }
12050             count = count || 1;
12051             duration = o.duration || 1;
12052             this.show();
12053
12054             var b = this.getBox();
12055             var animFn = function(){
12056                 var proxy = this.createProxy({
12057
12058                      style:{
12059                         visbility:"hidden",
12060                         position:"absolute",
12061                         "z-index":"35000", // yee haw
12062                         border:"0px solid " + color
12063                      }
12064                   });
12065                 var scale = Roo.isBorderBox ? 2 : 1;
12066                 proxy.animate({
12067                     top:{from:b.y, to:b.y - 20},
12068                     left:{from:b.x, to:b.x - 20},
12069                     borderWidth:{from:0, to:10},
12070                     opacity:{from:1, to:0},
12071                     height:{from:b.height, to:(b.height + (20*scale))},
12072                     width:{from:b.width, to:(b.width + (20*scale))}
12073                 }, duration, function(){
12074                     proxy.remove();
12075                 });
12076                 if(--count > 0){
12077                      animFn.defer((duration/2)*1000, this);
12078                 }else{
12079                     el.afterFx(o);
12080                 }
12081             };
12082             animFn.call(this);
12083         });
12084         return this;
12085     },
12086
12087    /**
12088     * Creates a pause before any subsequent queued effects begin.  If there are
12089     * no effects queued after the pause it will have no effect.
12090     * Usage:
12091 <pre><code>
12092 el.pause(1);
12093 </code></pre>
12094     * @param {Number} seconds The length of time to pause (in seconds)
12095     * @return {Roo.Element} The Element
12096     */
12097     pause : function(seconds){
12098         var el = this.getFxEl();
12099         var o = {};
12100
12101         el.queueFx(o, function(){
12102             setTimeout(function(){
12103                 el.afterFx(o);
12104             }, seconds * 1000);
12105         });
12106         return this;
12107     },
12108
12109    /**
12110     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
12111     * using the "endOpacity" config option.
12112     * Usage:
12113 <pre><code>
12114 // default: fade in from opacity 0 to 100%
12115 el.fadeIn();
12116
12117 // custom: fade in from opacity 0 to 75% over 2 seconds
12118 el.fadeIn({ endOpacity: .75, duration: 2});
12119
12120 // common config options shown with default values
12121 el.fadeIn({
12122     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12123     easing: 'easeOut',
12124     duration: .5
12125 });
12126 </code></pre>
12127     * @param {Object} options (optional) Object literal with any of the Fx config options
12128     * @return {Roo.Element} The Element
12129     */
12130     fadeIn : function(o){
12131         var el = this.getFxEl();
12132         o = o || {};
12133         el.queueFx(o, function(){
12134             this.setOpacity(0);
12135             this.fixDisplay();
12136             this.dom.style.visibility = 'visible';
12137             var to = o.endOpacity || 1;
12138             arguments.callee.anim = this.fxanim({opacity:{to:to}},
12139                 o, null, .5, "easeOut", function(){
12140                 if(to == 1){
12141                     this.clearOpacity();
12142                 }
12143                 el.afterFx(o);
12144             });
12145         });
12146         return this;
12147     },
12148
12149    /**
12150     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
12151     * using the "endOpacity" config option.
12152     * Usage:
12153 <pre><code>
12154 // default: fade out from the element's current opacity to 0
12155 el.fadeOut();
12156
12157 // custom: fade out from the element's current opacity to 25% over 2 seconds
12158 el.fadeOut({ endOpacity: .25, duration: 2});
12159
12160 // common config options shown with default values
12161 el.fadeOut({
12162     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12163     easing: 'easeOut',
12164     duration: .5
12165     remove: false,
12166     useDisplay: false
12167 });
12168 </code></pre>
12169     * @param {Object} options (optional) Object literal with any of the Fx config options
12170     * @return {Roo.Element} The Element
12171     */
12172     fadeOut : function(o){
12173         var el = this.getFxEl();
12174         o = o || {};
12175         el.queueFx(o, function(){
12176             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12177                 o, null, .5, "easeOut", function(){
12178                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12179                      this.dom.style.display = "none";
12180                 }else{
12181                      this.dom.style.visibility = "hidden";
12182                 }
12183                 this.clearOpacity();
12184                 el.afterFx(o);
12185             });
12186         });
12187         return this;
12188     },
12189
12190    /**
12191     * Animates the transition of an element's dimensions from a starting height/width
12192     * to an ending height/width.
12193     * Usage:
12194 <pre><code>
12195 // change height and width to 100x100 pixels
12196 el.scale(100, 100);
12197
12198 // common config options shown with default values.  The height and width will default to
12199 // the element's existing values if passed as null.
12200 el.scale(
12201     [element's width],
12202     [element's height], {
12203     easing: 'easeOut',
12204     duration: .35
12205 });
12206 </code></pre>
12207     * @param {Number} width  The new width (pass undefined to keep the original width)
12208     * @param {Number} height  The new height (pass undefined to keep the original height)
12209     * @param {Object} options (optional) Object literal with any of the Fx config options
12210     * @return {Roo.Element} The Element
12211     */
12212     scale : function(w, h, o){
12213         this.shift(Roo.apply({}, o, {
12214             width: w,
12215             height: h
12216         }));
12217         return this;
12218     },
12219
12220    /**
12221     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12222     * Any of these properties not specified in the config object will not be changed.  This effect 
12223     * requires that at least one new dimension, position or opacity setting must be passed in on
12224     * the config object in order for the function to have any effect.
12225     * Usage:
12226 <pre><code>
12227 // slide the element horizontally to x position 200 while changing the height and opacity
12228 el.shift({ x: 200, height: 50, opacity: .8 });
12229
12230 // common config options shown with default values.
12231 el.shift({
12232     width: [element's width],
12233     height: [element's height],
12234     x: [element's x position],
12235     y: [element's y position],
12236     opacity: [element's opacity],
12237     easing: 'easeOut',
12238     duration: .35
12239 });
12240 </code></pre>
12241     * @param {Object} options  Object literal with any of the Fx config options
12242     * @return {Roo.Element} The Element
12243     */
12244     shift : function(o){
12245         var el = this.getFxEl();
12246         o = o || {};
12247         el.queueFx(o, function(){
12248             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12249             if(w !== undefined){
12250                 a.width = {to: this.adjustWidth(w)};
12251             }
12252             if(h !== undefined){
12253                 a.height = {to: this.adjustHeight(h)};
12254             }
12255             if(x !== undefined || y !== undefined){
12256                 a.points = {to: [
12257                     x !== undefined ? x : this.getX(),
12258                     y !== undefined ? y : this.getY()
12259                 ]};
12260             }
12261             if(op !== undefined){
12262                 a.opacity = {to: op};
12263             }
12264             if(o.xy !== undefined){
12265                 a.points = {to: o.xy};
12266             }
12267             arguments.callee.anim = this.fxanim(a,
12268                 o, 'motion', .35, "easeOut", function(){
12269                 el.afterFx(o);
12270             });
12271         });
12272         return this;
12273     },
12274
12275         /**
12276          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12277          * ending point of the effect.
12278          * Usage:
12279          *<pre><code>
12280 // default: slide the element downward while fading out
12281 el.ghost();
12282
12283 // custom: slide the element out to the right with a 2-second duration
12284 el.ghost('r', { duration: 2 });
12285
12286 // common config options shown with default values
12287 el.ghost('b', {
12288     easing: 'easeOut',
12289     duration: .5
12290     remove: false,
12291     useDisplay: false
12292 });
12293 </code></pre>
12294          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12295          * @param {Object} options (optional) Object literal with any of the Fx config options
12296          * @return {Roo.Element} The Element
12297          */
12298     ghost : function(anchor, o){
12299         var el = this.getFxEl();
12300         o = o || {};
12301
12302         el.queueFx(o, function(){
12303             anchor = anchor || "b";
12304
12305             // restore values after effect
12306             var r = this.getFxRestore();
12307             var w = this.getWidth(),
12308                 h = this.getHeight();
12309
12310             var st = this.dom.style;
12311
12312             var after = function(){
12313                 if(o.useDisplay){
12314                     el.setDisplayed(false);
12315                 }else{
12316                     el.hide();
12317                 }
12318
12319                 el.clearOpacity();
12320                 el.setPositioning(r.pos);
12321                 st.width = r.width;
12322                 st.height = r.height;
12323
12324                 el.afterFx(o);
12325             };
12326
12327             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12328             switch(anchor.toLowerCase()){
12329                 case "t":
12330                     pt.by = [0, -h];
12331                 break;
12332                 case "l":
12333                     pt.by = [-w, 0];
12334                 break;
12335                 case "r":
12336                     pt.by = [w, 0];
12337                 break;
12338                 case "b":
12339                     pt.by = [0, h];
12340                 break;
12341                 case "tl":
12342                     pt.by = [-w, -h];
12343                 break;
12344                 case "bl":
12345                     pt.by = [-w, h];
12346                 break;
12347                 case "br":
12348                     pt.by = [w, h];
12349                 break;
12350                 case "tr":
12351                     pt.by = [w, -h];
12352                 break;
12353             }
12354
12355             arguments.callee.anim = this.fxanim(a,
12356                 o,
12357                 'motion',
12358                 .5,
12359                 "easeOut", after);
12360         });
12361         return this;
12362     },
12363
12364         /**
12365          * Ensures that all effects queued after syncFx is called on the element are
12366          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12367          * @return {Roo.Element} The Element
12368          */
12369     syncFx : function(){
12370         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12371             block : false,
12372             concurrent : true,
12373             stopFx : false
12374         });
12375         return this;
12376     },
12377
12378         /**
12379          * Ensures that all effects queued after sequenceFx is called on the element are
12380          * run in sequence.  This is the opposite of {@link #syncFx}.
12381          * @return {Roo.Element} The Element
12382          */
12383     sequenceFx : function(){
12384         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12385             block : false,
12386             concurrent : false,
12387             stopFx : false
12388         });
12389         return this;
12390     },
12391
12392         /* @private */
12393     nextFx : function(){
12394         var ef = this.fxQueue[0];
12395         if(ef){
12396             ef.call(this);
12397         }
12398     },
12399
12400         /**
12401          * Returns true if the element has any effects actively running or queued, else returns false.
12402          * @return {Boolean} True if element has active effects, else false
12403          */
12404     hasActiveFx : function(){
12405         return this.fxQueue && this.fxQueue[0];
12406     },
12407
12408         /**
12409          * Stops any running effects and clears the element's internal effects queue if it contains
12410          * any additional effects that haven't started yet.
12411          * @return {Roo.Element} The Element
12412          */
12413     stopFx : function(){
12414         if(this.hasActiveFx()){
12415             var cur = this.fxQueue[0];
12416             if(cur && cur.anim && cur.anim.isAnimated()){
12417                 this.fxQueue = [cur]; // clear out others
12418                 cur.anim.stop(true);
12419             }
12420         }
12421         return this;
12422     },
12423
12424         /* @private */
12425     beforeFx : function(o){
12426         if(this.hasActiveFx() && !o.concurrent){
12427            if(o.stopFx){
12428                this.stopFx();
12429                return true;
12430            }
12431            return false;
12432         }
12433         return true;
12434     },
12435
12436         /**
12437          * Returns true if the element is currently blocking so that no other effect can be queued
12438          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12439          * used to ensure that an effect initiated by a user action runs to completion prior to the
12440          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12441          * @return {Boolean} True if blocking, else false
12442          */
12443     hasFxBlock : function(){
12444         var q = this.fxQueue;
12445         return q && q[0] && q[0].block;
12446     },
12447
12448         /* @private */
12449     queueFx : function(o, fn){
12450         if(!this.fxQueue){
12451             this.fxQueue = [];
12452         }
12453         if(!this.hasFxBlock()){
12454             Roo.applyIf(o, this.fxDefaults);
12455             if(!o.concurrent){
12456                 var run = this.beforeFx(o);
12457                 fn.block = o.block;
12458                 this.fxQueue.push(fn);
12459                 if(run){
12460                     this.nextFx();
12461                 }
12462             }else{
12463                 fn.call(this);
12464             }
12465         }
12466         return this;
12467     },
12468
12469         /* @private */
12470     fxWrap : function(pos, o, vis){
12471         var wrap;
12472         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12473             var wrapXY;
12474             if(o.fixPosition){
12475                 wrapXY = this.getXY();
12476             }
12477             var div = document.createElement("div");
12478             div.style.visibility = vis;
12479             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12480             wrap.setPositioning(pos);
12481             if(wrap.getStyle("position") == "static"){
12482                 wrap.position("relative");
12483             }
12484             this.clearPositioning('auto');
12485             wrap.clip();
12486             wrap.dom.appendChild(this.dom);
12487             if(wrapXY){
12488                 wrap.setXY(wrapXY);
12489             }
12490         }
12491         return wrap;
12492     },
12493
12494         /* @private */
12495     fxUnwrap : function(wrap, pos, o){
12496         this.clearPositioning();
12497         this.setPositioning(pos);
12498         if(!o.wrap){
12499             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12500             wrap.remove();
12501         }
12502     },
12503
12504         /* @private */
12505     getFxRestore : function(){
12506         var st = this.dom.style;
12507         return {pos: this.getPositioning(), width: st.width, height : st.height};
12508     },
12509
12510         /* @private */
12511     afterFx : function(o){
12512         if(o.afterStyle){
12513             this.applyStyles(o.afterStyle);
12514         }
12515         if(o.afterCls){
12516             this.addClass(o.afterCls);
12517         }
12518         if(o.remove === true){
12519             this.remove();
12520         }
12521         Roo.callback(o.callback, o.scope, [this]);
12522         if(!o.concurrent){
12523             this.fxQueue.shift();
12524             this.nextFx();
12525         }
12526     },
12527
12528         /* @private */
12529     getFxEl : function(){ // support for composite element fx
12530         return Roo.get(this.dom);
12531     },
12532
12533         /* @private */
12534     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12535         animType = animType || 'run';
12536         opt = opt || {};
12537         var anim = Roo.lib.Anim[animType](
12538             this.dom, args,
12539             (opt.duration || defaultDur) || .35,
12540             (opt.easing || defaultEase) || 'easeOut',
12541             function(){
12542                 Roo.callback(cb, this);
12543             },
12544             this
12545         );
12546         opt.anim = anim;
12547         return anim;
12548     }
12549 };
12550
12551 // backwords compat
12552 Roo.Fx.resize = Roo.Fx.scale;
12553
12554 //When included, Roo.Fx is automatically applied to Element so that all basic
12555 //effects are available directly via the Element API
12556 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12557  * Based on:
12558  * Ext JS Library 1.1.1
12559  * Copyright(c) 2006-2007, Ext JS, LLC.
12560  *
12561  * Originally Released Under LGPL - original licence link has changed is not relivant.
12562  *
12563  * Fork - LGPL
12564  * <script type="text/javascript">
12565  */
12566
12567
12568 /**
12569  * @class Roo.CompositeElement
12570  * Standard composite class. Creates a Roo.Element for every element in the collection.
12571  * <br><br>
12572  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12573  * actions will be performed on all the elements in this collection.</b>
12574  * <br><br>
12575  * All methods return <i>this</i> and can be chained.
12576  <pre><code>
12577  var els = Roo.select("#some-el div.some-class", true);
12578  // or select directly from an existing element
12579  var el = Roo.get('some-el');
12580  el.select('div.some-class', true);
12581
12582  els.setWidth(100); // all elements become 100 width
12583  els.hide(true); // all elements fade out and hide
12584  // or
12585  els.setWidth(100).hide(true);
12586  </code></pre>
12587  */
12588 Roo.CompositeElement = function(els){
12589     this.elements = [];
12590     this.addElements(els);
12591 };
12592 Roo.CompositeElement.prototype = {
12593     isComposite: true,
12594     addElements : function(els){
12595         if(!els) {
12596             return this;
12597         }
12598         if(typeof els == "string"){
12599             els = Roo.Element.selectorFunction(els);
12600         }
12601         var yels = this.elements;
12602         var index = yels.length-1;
12603         for(var i = 0, len = els.length; i < len; i++) {
12604                 yels[++index] = Roo.get(els[i]);
12605         }
12606         return this;
12607     },
12608
12609     /**
12610     * Clears this composite and adds the elements returned by the passed selector.
12611     * @param {String/Array} els A string CSS selector, an array of elements or an element
12612     * @return {CompositeElement} this
12613     */
12614     fill : function(els){
12615         this.elements = [];
12616         this.add(els);
12617         return this;
12618     },
12619
12620     /**
12621     * Filters this composite to only elements that match the passed selector.
12622     * @param {String} selector A string CSS selector
12623     * @param {Boolean} inverse return inverse filter (not matches)
12624     * @return {CompositeElement} this
12625     */
12626     filter : function(selector, inverse){
12627         var els = [];
12628         inverse = inverse || false;
12629         this.each(function(el){
12630             var match = inverse ? !el.is(selector) : el.is(selector);
12631             if(match){
12632                 els[els.length] = el.dom;
12633             }
12634         });
12635         this.fill(els);
12636         return this;
12637     },
12638
12639     invoke : function(fn, args){
12640         var els = this.elements;
12641         for(var i = 0, len = els.length; i < len; i++) {
12642                 Roo.Element.prototype[fn].apply(els[i], args);
12643         }
12644         return this;
12645     },
12646     /**
12647     * Adds elements to this composite.
12648     * @param {String/Array} els A string CSS selector, an array of elements or an element
12649     * @return {CompositeElement} this
12650     */
12651     add : function(els){
12652         if(typeof els == "string"){
12653             this.addElements(Roo.Element.selectorFunction(els));
12654         }else if(els.length !== undefined){
12655             this.addElements(els);
12656         }else{
12657             this.addElements([els]);
12658         }
12659         return this;
12660     },
12661     /**
12662     * Calls the passed function passing (el, this, index) for each element in this composite.
12663     * @param {Function} fn The function to call
12664     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12665     * @return {CompositeElement} this
12666     */
12667     each : function(fn, scope){
12668         var els = this.elements;
12669         for(var i = 0, len = els.length; i < len; i++){
12670             if(fn.call(scope || els[i], els[i], this, i) === false) {
12671                 break;
12672             }
12673         }
12674         return this;
12675     },
12676
12677     /**
12678      * Returns the Element object at the specified index
12679      * @param {Number} index
12680      * @return {Roo.Element}
12681      */
12682     item : function(index){
12683         return this.elements[index] || null;
12684     },
12685
12686     /**
12687      * Returns the first Element
12688      * @return {Roo.Element}
12689      */
12690     first : function(){
12691         return this.item(0);
12692     },
12693
12694     /**
12695      * Returns the last Element
12696      * @return {Roo.Element}
12697      */
12698     last : function(){
12699         return this.item(this.elements.length-1);
12700     },
12701
12702     /**
12703      * Returns the number of elements in this composite
12704      * @return Number
12705      */
12706     getCount : function(){
12707         return this.elements.length;
12708     },
12709
12710     /**
12711      * Returns true if this composite contains the passed element
12712      * @return Boolean
12713      */
12714     contains : function(el){
12715         return this.indexOf(el) !== -1;
12716     },
12717
12718     /**
12719      * Returns true if this composite contains the passed element
12720      * @return Boolean
12721      */
12722     indexOf : function(el){
12723         return this.elements.indexOf(Roo.get(el));
12724     },
12725
12726
12727     /**
12728     * Removes the specified element(s).
12729     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12730     * or an array of any of those.
12731     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12732     * @return {CompositeElement} this
12733     */
12734     removeElement : function(el, removeDom){
12735         if(el instanceof Array){
12736             for(var i = 0, len = el.length; i < len; i++){
12737                 this.removeElement(el[i]);
12738             }
12739             return this;
12740         }
12741         var index = typeof el == 'number' ? el : this.indexOf(el);
12742         if(index !== -1){
12743             if(removeDom){
12744                 var d = this.elements[index];
12745                 if(d.dom){
12746                     d.remove();
12747                 }else{
12748                     d.parentNode.removeChild(d);
12749                 }
12750             }
12751             this.elements.splice(index, 1);
12752         }
12753         return this;
12754     },
12755
12756     /**
12757     * Replaces the specified element with the passed element.
12758     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12759     * to replace.
12760     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12761     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12762     * @return {CompositeElement} this
12763     */
12764     replaceElement : function(el, replacement, domReplace){
12765         var index = typeof el == 'number' ? el : this.indexOf(el);
12766         if(index !== -1){
12767             if(domReplace){
12768                 this.elements[index].replaceWith(replacement);
12769             }else{
12770                 this.elements.splice(index, 1, Roo.get(replacement))
12771             }
12772         }
12773         return this;
12774     },
12775
12776     /**
12777      * Removes all elements.
12778      */
12779     clear : function(){
12780         this.elements = [];
12781     }
12782 };
12783 (function(){
12784     Roo.CompositeElement.createCall = function(proto, fnName){
12785         if(!proto[fnName]){
12786             proto[fnName] = function(){
12787                 return this.invoke(fnName, arguments);
12788             };
12789         }
12790     };
12791     for(var fnName in Roo.Element.prototype){
12792         if(typeof Roo.Element.prototype[fnName] == "function"){
12793             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12794         }
12795     };
12796 })();
12797 /*
12798  * Based on:
12799  * Ext JS Library 1.1.1
12800  * Copyright(c) 2006-2007, Ext JS, LLC.
12801  *
12802  * Originally Released Under LGPL - original licence link has changed is not relivant.
12803  *
12804  * Fork - LGPL
12805  * <script type="text/javascript">
12806  */
12807
12808 /**
12809  * @class Roo.CompositeElementLite
12810  * @extends Roo.CompositeElement
12811  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12812  <pre><code>
12813  var els = Roo.select("#some-el div.some-class");
12814  // or select directly from an existing element
12815  var el = Roo.get('some-el');
12816  el.select('div.some-class');
12817
12818  els.setWidth(100); // all elements become 100 width
12819  els.hide(true); // all elements fade out and hide
12820  // or
12821  els.setWidth(100).hide(true);
12822  </code></pre><br><br>
12823  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12824  * actions will be performed on all the elements in this collection.</b>
12825  */
12826 Roo.CompositeElementLite = function(els){
12827     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12828     this.el = new Roo.Element.Flyweight();
12829 };
12830 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12831     addElements : function(els){
12832         if(els){
12833             if(els instanceof Array){
12834                 this.elements = this.elements.concat(els);
12835             }else{
12836                 var yels = this.elements;
12837                 var index = yels.length-1;
12838                 for(var i = 0, len = els.length; i < len; i++) {
12839                     yels[++index] = els[i];
12840                 }
12841             }
12842         }
12843         return this;
12844     },
12845     invoke : function(fn, args){
12846         var els = this.elements;
12847         var el = this.el;
12848         for(var i = 0, len = els.length; i < len; i++) {
12849             el.dom = els[i];
12850                 Roo.Element.prototype[fn].apply(el, args);
12851         }
12852         return this;
12853     },
12854     /**
12855      * Returns a flyweight Element of the dom element object at the specified index
12856      * @param {Number} index
12857      * @return {Roo.Element}
12858      */
12859     item : function(index){
12860         if(!this.elements[index]){
12861             return null;
12862         }
12863         this.el.dom = this.elements[index];
12864         return this.el;
12865     },
12866
12867     // fixes scope with flyweight
12868     addListener : function(eventName, handler, scope, opt){
12869         var els = this.elements;
12870         for(var i = 0, len = els.length; i < len; i++) {
12871             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12872         }
12873         return this;
12874     },
12875
12876     /**
12877     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12878     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12879     * a reference to the dom node, use el.dom.</b>
12880     * @param {Function} fn The function to call
12881     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12882     * @return {CompositeElement} this
12883     */
12884     each : function(fn, scope){
12885         var els = this.elements;
12886         var el = this.el;
12887         for(var i = 0, len = els.length; i < len; i++){
12888             el.dom = els[i];
12889                 if(fn.call(scope || el, el, this, i) === false){
12890                 break;
12891             }
12892         }
12893         return this;
12894     },
12895
12896     indexOf : function(el){
12897         return this.elements.indexOf(Roo.getDom(el));
12898     },
12899
12900     replaceElement : function(el, replacement, domReplace){
12901         var index = typeof el == 'number' ? el : this.indexOf(el);
12902         if(index !== -1){
12903             replacement = Roo.getDom(replacement);
12904             if(domReplace){
12905                 var d = this.elements[index];
12906                 d.parentNode.insertBefore(replacement, d);
12907                 d.parentNode.removeChild(d);
12908             }
12909             this.elements.splice(index, 1, replacement);
12910         }
12911         return this;
12912     }
12913 });
12914 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12915
12916 /*
12917  * Based on:
12918  * Ext JS Library 1.1.1
12919  * Copyright(c) 2006-2007, Ext JS, LLC.
12920  *
12921  * Originally Released Under LGPL - original licence link has changed is not relivant.
12922  *
12923  * Fork - LGPL
12924  * <script type="text/javascript">
12925  */
12926
12927  
12928
12929 /**
12930  * @class Roo.data.Connection
12931  * @extends Roo.util.Observable
12932  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12933  * either to a configured URL, or to a URL specified at request time. 
12934  * 
12935  * Requests made by this class are asynchronous, and will return immediately. No data from
12936  * the server will be available to the statement immediately following the {@link #request} call.
12937  * To process returned data, use a callback in the request options object, or an event listener.
12938  * 
12939  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12940  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12941  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12942  * property and, if present, the IFRAME's XML document as the responseXML property.
12943  * 
12944  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12945  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12946  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12947  * standard DOM methods.
12948  * @constructor
12949  * @param {Object} config a configuration object.
12950  */
12951 Roo.data.Connection = function(config){
12952     Roo.apply(this, config);
12953     this.addEvents({
12954         /**
12955          * @event beforerequest
12956          * Fires before a network request is made to retrieve a data object.
12957          * @param {Connection} conn This Connection object.
12958          * @param {Object} options The options config object passed to the {@link #request} method.
12959          */
12960         "beforerequest" : true,
12961         /**
12962          * @event requestcomplete
12963          * Fires if the request was successfully completed.
12964          * @param {Connection} conn This Connection object.
12965          * @param {Object} response The XHR object containing the response data.
12966          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12967          * @param {Object} options The options config object passed to the {@link #request} method.
12968          */
12969         "requestcomplete" : true,
12970         /**
12971          * @event requestexception
12972          * Fires if an error HTTP status was returned from the server.
12973          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12974          * @param {Connection} conn This Connection object.
12975          * @param {Object} response The XHR object containing the response data.
12976          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12977          * @param {Object} options The options config object passed to the {@link #request} method.
12978          */
12979         "requestexception" : true
12980     });
12981     Roo.data.Connection.superclass.constructor.call(this);
12982 };
12983
12984 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12985     /**
12986      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12987      */
12988     /**
12989      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12990      * extra parameters to each request made by this object. (defaults to undefined)
12991      */
12992     /**
12993      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12994      *  to each request made by this object. (defaults to undefined)
12995      */
12996     /**
12997      * @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)
12998      */
12999     /**
13000      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13001      */
13002     timeout : 30000,
13003     /**
13004      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13005      * @type Boolean
13006      */
13007     autoAbort:false,
13008
13009     /**
13010      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13011      * @type Boolean
13012      */
13013     disableCaching: true,
13014
13015     /**
13016      * Sends an HTTP request to a remote server.
13017      * @param {Object} options An object which may contain the following properties:<ul>
13018      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13019      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13020      * request, a url encoded string or a function to call to get either.</li>
13021      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13022      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13023      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13024      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13025      * <li>options {Object} The parameter to the request call.</li>
13026      * <li>success {Boolean} True if the request succeeded.</li>
13027      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13028      * </ul></li>
13029      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13030      * The callback is passed the following parameters:<ul>
13031      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13032      * <li>options {Object} The parameter to the request call.</li>
13033      * </ul></li>
13034      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13035      * The callback is passed the following parameters:<ul>
13036      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13037      * <li>options {Object} The parameter to the request call.</li>
13038      * </ul></li>
13039      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13040      * for the callback function. Defaults to the browser window.</li>
13041      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13042      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13043      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13044      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13045      * params for the post data. Any params will be appended to the URL.</li>
13046      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13047      * </ul>
13048      * @return {Number} transactionId
13049      */
13050     request : function(o){
13051         if(this.fireEvent("beforerequest", this, o) !== false){
13052             var p = o.params;
13053
13054             if(typeof p == "function"){
13055                 p = p.call(o.scope||window, o);
13056             }
13057             if(typeof p == "object"){
13058                 p = Roo.urlEncode(o.params);
13059             }
13060             if(this.extraParams){
13061                 var extras = Roo.urlEncode(this.extraParams);
13062                 p = p ? (p + '&' + extras) : extras;
13063             }
13064
13065             var url = o.url || this.url;
13066             if(typeof url == 'function'){
13067                 url = url.call(o.scope||window, o);
13068             }
13069
13070             if(o.form){
13071                 var form = Roo.getDom(o.form);
13072                 url = url || form.action;
13073
13074                 var enctype = form.getAttribute("enctype");
13075                 
13076                 if (o.formData) {
13077                     return this.doFormDataUpload(o, url);
13078                 }
13079                 
13080                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13081                     return this.doFormUpload(o, p, url);
13082                 }
13083                 var f = Roo.lib.Ajax.serializeForm(form);
13084                 p = p ? (p + '&' + f) : f;
13085             }
13086             
13087             if (!o.form && o.formData) {
13088                 o.formData = o.formData === true ? new FormData() : o.formData;
13089                 for (var k in o.params) {
13090                     o.formData.append(k,o.params[k]);
13091                 }
13092                     
13093                 return this.doFormDataUpload(o, url);
13094             }
13095             
13096
13097             var hs = o.headers;
13098             if(this.defaultHeaders){
13099                 hs = Roo.apply(hs || {}, this.defaultHeaders);
13100                 if(!o.headers){
13101                     o.headers = hs;
13102                 }
13103             }
13104
13105             var cb = {
13106                 success: this.handleResponse,
13107                 failure: this.handleFailure,
13108                 scope: this,
13109                 argument: {options: o},
13110                 timeout : o.timeout || this.timeout
13111             };
13112
13113             var method = o.method||this.method||(p ? "POST" : "GET");
13114
13115             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13116                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13117             }
13118
13119             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13120                 if(o.autoAbort){
13121                     this.abort();
13122                 }
13123             }else if(this.autoAbort !== false){
13124                 this.abort();
13125             }
13126
13127             if((method == 'GET' && p) || o.xmlData){
13128                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13129                 p = '';
13130             }
13131             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13132             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13133             Roo.lib.Ajax.useDefaultHeader == true;
13134             return this.transId;
13135         }else{
13136             Roo.callback(o.callback, o.scope, [o, null, null]);
13137             return null;
13138         }
13139     },
13140
13141     /**
13142      * Determine whether this object has a request outstanding.
13143      * @param {Number} transactionId (Optional) defaults to the last transaction
13144      * @return {Boolean} True if there is an outstanding request.
13145      */
13146     isLoading : function(transId){
13147         if(transId){
13148             return Roo.lib.Ajax.isCallInProgress(transId);
13149         }else{
13150             return this.transId ? true : false;
13151         }
13152     },
13153
13154     /**
13155      * Aborts any outstanding request.
13156      * @param {Number} transactionId (Optional) defaults to the last transaction
13157      */
13158     abort : function(transId){
13159         if(transId || this.isLoading()){
13160             Roo.lib.Ajax.abort(transId || this.transId);
13161         }
13162     },
13163
13164     // private
13165     handleResponse : function(response){
13166         this.transId = false;
13167         var options = response.argument.options;
13168         response.argument = options ? options.argument : null;
13169         this.fireEvent("requestcomplete", this, response, options);
13170         Roo.callback(options.success, options.scope, [response, options]);
13171         Roo.callback(options.callback, options.scope, [options, true, response]);
13172     },
13173
13174     // private
13175     handleFailure : function(response, e){
13176         this.transId = false;
13177         var options = response.argument.options;
13178         response.argument = options ? options.argument : null;
13179         this.fireEvent("requestexception", this, response, options, e);
13180         Roo.callback(options.failure, options.scope, [response, options]);
13181         Roo.callback(options.callback, options.scope, [options, false, response]);
13182     },
13183
13184     // private
13185     doFormUpload : function(o, ps, url){
13186         var id = Roo.id();
13187         var frame = document.createElement('iframe');
13188         frame.id = id;
13189         frame.name = id;
13190         frame.className = 'x-hidden';
13191         if(Roo.isIE){
13192             frame.src = Roo.SSL_SECURE_URL;
13193         }
13194         document.body.appendChild(frame);
13195
13196         if(Roo.isIE){
13197            document.frames[id].name = id;
13198         }
13199
13200         var form = Roo.getDom(o.form);
13201         form.target = id;
13202         form.method = 'POST';
13203         form.enctype = form.encoding = 'multipart/form-data';
13204         if(url){
13205             form.action = url;
13206         }
13207
13208         var hiddens, hd;
13209         if(ps){ // add dynamic params
13210             hiddens = [];
13211             ps = Roo.urlDecode(ps, false);
13212             for(var k in ps){
13213                 if(ps.hasOwnProperty(k)){
13214                     hd = document.createElement('input');
13215                     hd.type = 'hidden';
13216                     hd.name = k;
13217                     hd.value = ps[k];
13218                     form.appendChild(hd);
13219                     hiddens.push(hd);
13220                 }
13221             }
13222         }
13223
13224         function cb(){
13225             var r = {  // bogus response object
13226                 responseText : '',
13227                 responseXML : null
13228             };
13229
13230             r.argument = o ? o.argument : null;
13231
13232             try { //
13233                 var doc;
13234                 if(Roo.isIE){
13235                     doc = frame.contentWindow.document;
13236                 }else {
13237                     doc = (frame.contentDocument || window.frames[id].document);
13238                 }
13239                 if(doc && doc.body){
13240                     r.responseText = doc.body.innerHTML;
13241                 }
13242                 if(doc && doc.XMLDocument){
13243                     r.responseXML = doc.XMLDocument;
13244                 }else {
13245                     r.responseXML = doc;
13246                 }
13247             }
13248             catch(e) {
13249                 // ignore
13250             }
13251
13252             Roo.EventManager.removeListener(frame, 'load', cb, this);
13253
13254             this.fireEvent("requestcomplete", this, r, o);
13255             Roo.callback(o.success, o.scope, [r, o]);
13256             Roo.callback(o.callback, o.scope, [o, true, r]);
13257
13258             setTimeout(function(){document.body.removeChild(frame);}, 100);
13259         }
13260
13261         Roo.EventManager.on(frame, 'load', cb, this);
13262         form.submit();
13263
13264         if(hiddens){ // remove dynamic params
13265             for(var i = 0, len = hiddens.length; i < len; i++){
13266                 form.removeChild(hiddens[i]);
13267             }
13268         }
13269     },
13270     // this is a 'formdata version???'
13271     
13272     
13273     doFormDataUpload : function(o,  url)
13274     {
13275         var formData;
13276         if (o.form) {
13277             var form =  Roo.getDom(o.form);
13278             form.enctype = form.encoding = 'multipart/form-data';
13279             formData = o.formData === true ? new FormData(form) : o.formData;
13280         } else {
13281             formData = o.formData === true ? new FormData() : o.formData;
13282         }
13283         
13284       
13285         var cb = {
13286             success: this.handleResponse,
13287             failure: this.handleFailure,
13288             scope: this,
13289             argument: {options: o},
13290             timeout : o.timeout || this.timeout
13291         };
13292  
13293         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13294             if(o.autoAbort){
13295                 this.abort();
13296             }
13297         }else if(this.autoAbort !== false){
13298             this.abort();
13299         }
13300
13301         //Roo.lib.Ajax.defaultPostHeader = null;
13302         Roo.lib.Ajax.useDefaultHeader = false;
13303         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13304         Roo.lib.Ajax.useDefaultHeader = true;
13305  
13306          
13307     }
13308     
13309 });
13310 /*
13311  * Based on:
13312  * Ext JS Library 1.1.1
13313  * Copyright(c) 2006-2007, Ext JS, LLC.
13314  *
13315  * Originally Released Under LGPL - original licence link has changed is not relivant.
13316  *
13317  * Fork - LGPL
13318  * <script type="text/javascript">
13319  */
13320  
13321 /**
13322  * Global Ajax request class.
13323  * 
13324  * @class Roo.Ajax
13325  * @extends Roo.data.Connection
13326  * @static
13327  * 
13328  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13329  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13330  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13331  * @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)
13332  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13333  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13334  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13335  */
13336 Roo.Ajax = new Roo.data.Connection({
13337     // fix up the docs
13338     /**
13339      * @scope Roo.Ajax
13340      * @type {Boolear} 
13341      */
13342     autoAbort : false,
13343
13344     /**
13345      * Serialize the passed form into a url encoded string
13346      * @scope Roo.Ajax
13347      * @param {String/HTMLElement} form
13348      * @return {String}
13349      */
13350     serializeForm : function(form){
13351         return Roo.lib.Ajax.serializeForm(form);
13352     }
13353 });/*
13354  * Based on:
13355  * Ext JS Library 1.1.1
13356  * Copyright(c) 2006-2007, Ext JS, LLC.
13357  *
13358  * Originally Released Under LGPL - original licence link has changed is not relivant.
13359  *
13360  * Fork - LGPL
13361  * <script type="text/javascript">
13362  */
13363
13364  
13365 /**
13366  * @class Roo.UpdateManager
13367  * @extends Roo.util.Observable
13368  * Provides AJAX-style update for Element object.<br><br>
13369  * Usage:<br>
13370  * <pre><code>
13371  * // Get it from a Roo.Element object
13372  * var el = Roo.get("foo");
13373  * var mgr = el.getUpdateManager();
13374  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13375  * ...
13376  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13377  * <br>
13378  * // or directly (returns the same UpdateManager instance)
13379  * var mgr = new Roo.UpdateManager("myElementId");
13380  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13381  * mgr.on("update", myFcnNeedsToKnow);
13382  * <br>
13383    // short handed call directly from the element object
13384    Roo.get("foo").load({
13385         url: "bar.php",
13386         scripts:true,
13387         params: "for=bar",
13388         text: "Loading Foo..."
13389    });
13390  * </code></pre>
13391  * @constructor
13392  * Create new UpdateManager directly.
13393  * @param {String/HTMLElement/Roo.Element} el The element to update
13394  * @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).
13395  */
13396 Roo.UpdateManager = function(el, forceNew){
13397     el = Roo.get(el);
13398     if(!forceNew && el.updateManager){
13399         return el.updateManager;
13400     }
13401     /**
13402      * The Element object
13403      * @type Roo.Element
13404      */
13405     this.el = el;
13406     /**
13407      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13408      * @type String
13409      */
13410     this.defaultUrl = null;
13411
13412     this.addEvents({
13413         /**
13414          * @event beforeupdate
13415          * Fired before an update is made, return false from your handler and the update is cancelled.
13416          * @param {Roo.Element} el
13417          * @param {String/Object/Function} url
13418          * @param {String/Object} params
13419          */
13420         "beforeupdate": true,
13421         /**
13422          * @event update
13423          * Fired after successful update is made.
13424          * @param {Roo.Element} el
13425          * @param {Object} oResponseObject The response Object
13426          */
13427         "update": true,
13428         /**
13429          * @event failure
13430          * Fired on update failure.
13431          * @param {Roo.Element} el
13432          * @param {Object} oResponseObject The response Object
13433          */
13434         "failure": true
13435     });
13436     var d = Roo.UpdateManager.defaults;
13437     /**
13438      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13439      * @type String
13440      */
13441     this.sslBlankUrl = d.sslBlankUrl;
13442     /**
13443      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13444      * @type Boolean
13445      */
13446     this.disableCaching = d.disableCaching;
13447     /**
13448      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13449      * @type String
13450      */
13451     this.indicatorText = d.indicatorText;
13452     /**
13453      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13454      * @type String
13455      */
13456     this.showLoadIndicator = d.showLoadIndicator;
13457     /**
13458      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13459      * @type Number
13460      */
13461     this.timeout = d.timeout;
13462
13463     /**
13464      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13465      * @type Boolean
13466      */
13467     this.loadScripts = d.loadScripts;
13468
13469     /**
13470      * Transaction object of current executing transaction
13471      */
13472     this.transaction = null;
13473
13474     /**
13475      * @private
13476      */
13477     this.autoRefreshProcId = null;
13478     /**
13479      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13480      * @type Function
13481      */
13482     this.refreshDelegate = this.refresh.createDelegate(this);
13483     /**
13484      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13485      * @type Function
13486      */
13487     this.updateDelegate = this.update.createDelegate(this);
13488     /**
13489      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13490      * @type Function
13491      */
13492     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13493     /**
13494      * @private
13495      */
13496     this.successDelegate = this.processSuccess.createDelegate(this);
13497     /**
13498      * @private
13499      */
13500     this.failureDelegate = this.processFailure.createDelegate(this);
13501
13502     if(!this.renderer){
13503      /**
13504       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13505       */
13506     this.renderer = new Roo.UpdateManager.BasicRenderer();
13507     }
13508     
13509     Roo.UpdateManager.superclass.constructor.call(this);
13510 };
13511
13512 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13513     /**
13514      * Get the Element this UpdateManager is bound to
13515      * @return {Roo.Element} The element
13516      */
13517     getEl : function(){
13518         return this.el;
13519     },
13520     /**
13521      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13522      * @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:
13523 <pre><code>
13524 um.update({<br/>
13525     url: "your-url.php",<br/>
13526     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13527     callback: yourFunction,<br/>
13528     scope: yourObject, //(optional scope)  <br/>
13529     discardUrl: false, <br/>
13530     nocache: false,<br/>
13531     text: "Loading...",<br/>
13532     timeout: 30,<br/>
13533     scripts: false<br/>
13534 });
13535 </code></pre>
13536      * The only required property is url. The optional properties nocache, text and scripts
13537      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13538      * @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}
13539      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13540      * @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.
13541      */
13542     update : function(url, params, callback, discardUrl){
13543         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13544             var method = this.method,
13545                 cfg;
13546             if(typeof url == "object"){ // must be config object
13547                 cfg = url;
13548                 url = cfg.url;
13549                 params = params || cfg.params;
13550                 callback = callback || cfg.callback;
13551                 discardUrl = discardUrl || cfg.discardUrl;
13552                 if(callback && cfg.scope){
13553                     callback = callback.createDelegate(cfg.scope);
13554                 }
13555                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13556                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13557                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13558                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13559                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13560             }
13561             this.showLoading();
13562             if(!discardUrl){
13563                 this.defaultUrl = url;
13564             }
13565             if(typeof url == "function"){
13566                 url = url.call(this);
13567             }
13568
13569             method = method || (params ? "POST" : "GET");
13570             if(method == "GET"){
13571                 url = this.prepareUrl(url);
13572             }
13573
13574             var o = Roo.apply(cfg ||{}, {
13575                 url : url,
13576                 params: params,
13577                 success: this.successDelegate,
13578                 failure: this.failureDelegate,
13579                 callback: undefined,
13580                 timeout: (this.timeout*1000),
13581                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13582             });
13583             Roo.log("updated manager called with timeout of " + o.timeout);
13584             this.transaction = Roo.Ajax.request(o);
13585         }
13586     },
13587
13588     /**
13589      * 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.
13590      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13591      * @param {String/HTMLElement} form The form Id or form element
13592      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13593      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13594      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13595      */
13596     formUpdate : function(form, url, reset, callback){
13597         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13598             if(typeof url == "function"){
13599                 url = url.call(this);
13600             }
13601             form = Roo.getDom(form);
13602             this.transaction = Roo.Ajax.request({
13603                 form: form,
13604                 url:url,
13605                 success: this.successDelegate,
13606                 failure: this.failureDelegate,
13607                 timeout: (this.timeout*1000),
13608                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13609             });
13610             this.showLoading.defer(1, this);
13611         }
13612     },
13613
13614     /**
13615      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13616      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13617      */
13618     refresh : function(callback){
13619         if(this.defaultUrl == null){
13620             return;
13621         }
13622         this.update(this.defaultUrl, null, callback, true);
13623     },
13624
13625     /**
13626      * Set this element to auto refresh.
13627      * @param {Number} interval How often to update (in seconds).
13628      * @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)
13629      * @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}
13630      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13631      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13632      */
13633     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13634         if(refreshNow){
13635             this.update(url || this.defaultUrl, params, callback, true);
13636         }
13637         if(this.autoRefreshProcId){
13638             clearInterval(this.autoRefreshProcId);
13639         }
13640         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13641     },
13642
13643     /**
13644      * Stop auto refresh on this element.
13645      */
13646      stopAutoRefresh : function(){
13647         if(this.autoRefreshProcId){
13648             clearInterval(this.autoRefreshProcId);
13649             delete this.autoRefreshProcId;
13650         }
13651     },
13652
13653     isAutoRefreshing : function(){
13654        return this.autoRefreshProcId ? true : false;
13655     },
13656     /**
13657      * Called to update the element to "Loading" state. Override to perform custom action.
13658      */
13659     showLoading : function(){
13660         if(this.showLoadIndicator){
13661             this.el.update(this.indicatorText);
13662         }
13663     },
13664
13665     /**
13666      * Adds unique parameter to query string if disableCaching = true
13667      * @private
13668      */
13669     prepareUrl : function(url){
13670         if(this.disableCaching){
13671             var append = "_dc=" + (new Date().getTime());
13672             if(url.indexOf("?") !== -1){
13673                 url += "&" + append;
13674             }else{
13675                 url += "?" + append;
13676             }
13677         }
13678         return url;
13679     },
13680
13681     /**
13682      * @private
13683      */
13684     processSuccess : function(response){
13685         this.transaction = null;
13686         if(response.argument.form && response.argument.reset){
13687             try{ // put in try/catch since some older FF releases had problems with this
13688                 response.argument.form.reset();
13689             }catch(e){}
13690         }
13691         if(this.loadScripts){
13692             this.renderer.render(this.el, response, this,
13693                 this.updateComplete.createDelegate(this, [response]));
13694         }else{
13695             this.renderer.render(this.el, response, this);
13696             this.updateComplete(response);
13697         }
13698     },
13699
13700     updateComplete : function(response){
13701         this.fireEvent("update", this.el, response);
13702         if(typeof response.argument.callback == "function"){
13703             response.argument.callback(this.el, true, response);
13704         }
13705     },
13706
13707     /**
13708      * @private
13709      */
13710     processFailure : function(response){
13711         this.transaction = null;
13712         this.fireEvent("failure", this.el, response);
13713         if(typeof response.argument.callback == "function"){
13714             response.argument.callback(this.el, false, response);
13715         }
13716     },
13717
13718     /**
13719      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13720      * @param {Object} renderer The object implementing the render() method
13721      */
13722     setRenderer : function(renderer){
13723         this.renderer = renderer;
13724     },
13725
13726     getRenderer : function(){
13727        return this.renderer;
13728     },
13729
13730     /**
13731      * Set the defaultUrl used for updates
13732      * @param {String/Function} defaultUrl The url or a function to call to get the url
13733      */
13734     setDefaultUrl : function(defaultUrl){
13735         this.defaultUrl = defaultUrl;
13736     },
13737
13738     /**
13739      * Aborts the executing transaction
13740      */
13741     abort : function(){
13742         if(this.transaction){
13743             Roo.Ajax.abort(this.transaction);
13744         }
13745     },
13746
13747     /**
13748      * Returns true if an update is in progress
13749      * @return {Boolean}
13750      */
13751     isUpdating : function(){
13752         if(this.transaction){
13753             return Roo.Ajax.isLoading(this.transaction);
13754         }
13755         return false;
13756     }
13757 });
13758
13759 /**
13760  * @class Roo.UpdateManager.defaults
13761  * @static (not really - but it helps the doc tool)
13762  * The defaults collection enables customizing the default properties of UpdateManager
13763  */
13764    Roo.UpdateManager.defaults = {
13765        /**
13766          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13767          * @type Number
13768          */
13769          timeout : 30,
13770
13771          /**
13772          * True to process scripts by default (Defaults to false).
13773          * @type Boolean
13774          */
13775         loadScripts : false,
13776
13777         /**
13778         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13779         * @type String
13780         */
13781         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13782         /**
13783          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13784          * @type Boolean
13785          */
13786         disableCaching : false,
13787         /**
13788          * Whether to show indicatorText when loading (Defaults to true).
13789          * @type Boolean
13790          */
13791         showLoadIndicator : true,
13792         /**
13793          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13794          * @type String
13795          */
13796         indicatorText : '<div class="loading-indicator">Loading...</div>'
13797    };
13798
13799 /**
13800  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13801  *Usage:
13802  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13803  * @param {String/HTMLElement/Roo.Element} el The element to update
13804  * @param {String} url The url
13805  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13806  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13807  * @static
13808  * @deprecated
13809  * @member Roo.UpdateManager
13810  */
13811 Roo.UpdateManager.updateElement = function(el, url, params, options){
13812     var um = Roo.get(el, true).getUpdateManager();
13813     Roo.apply(um, options);
13814     um.update(url, params, options ? options.callback : null);
13815 };
13816 // alias for backwards compat
13817 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13818 /**
13819  * @class Roo.UpdateManager.BasicRenderer
13820  * Default Content renderer. Updates the elements innerHTML with the responseText.
13821  */
13822 Roo.UpdateManager.BasicRenderer = function(){};
13823
13824 Roo.UpdateManager.BasicRenderer.prototype = {
13825     /**
13826      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13827      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13828      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13829      * @param {Roo.Element} el The element being rendered
13830      * @param {Object} response The YUI Connect response object
13831      * @param {UpdateManager} updateManager The calling update manager
13832      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13833      */
13834      render : function(el, response, updateManager, callback){
13835         el.update(response.responseText, updateManager.loadScripts, callback);
13836     }
13837 };
13838 /*
13839  * Based on:
13840  * Roo JS
13841  * (c)) Alan Knowles
13842  * Licence : LGPL
13843  */
13844
13845
13846 /**
13847  * @class Roo.DomTemplate
13848  * @extends Roo.Template
13849  * An effort at a dom based template engine..
13850  *
13851  * Similar to XTemplate, except it uses dom parsing to create the template..
13852  *
13853  * Supported features:
13854  *
13855  *  Tags:
13856
13857 <pre><code>
13858       {a_variable} - output encoded.
13859       {a_variable.format:("Y-m-d")} - call a method on the variable
13860       {a_variable:raw} - unencoded output
13861       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13862       {a_variable:this.method_on_template(...)} - call a method on the template object.
13863  
13864 </code></pre>
13865  *  The tpl tag:
13866 <pre><code>
13867         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13868         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13869         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13870         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13871   
13872 </code></pre>
13873  *      
13874  */
13875 Roo.DomTemplate = function()
13876 {
13877      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13878      if (this.html) {
13879         this.compile();
13880      }
13881 };
13882
13883
13884 Roo.extend(Roo.DomTemplate, Roo.Template, {
13885     /**
13886      * id counter for sub templates.
13887      */
13888     id : 0,
13889     /**
13890      * flag to indicate if dom parser is inside a pre,
13891      * it will strip whitespace if not.
13892      */
13893     inPre : false,
13894     
13895     /**
13896      * The various sub templates
13897      */
13898     tpls : false,
13899     
13900     
13901     
13902     /**
13903      *
13904      * basic tag replacing syntax
13905      * WORD:WORD()
13906      *
13907      * // you can fake an object call by doing this
13908      *  x.t:(test,tesT) 
13909      * 
13910      */
13911     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13912     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13913     
13914     iterChild : function (node, method) {
13915         
13916         var oldPre = this.inPre;
13917         if (node.tagName == 'PRE') {
13918             this.inPre = true;
13919         }
13920         for( var i = 0; i < node.childNodes.length; i++) {
13921             method.call(this, node.childNodes[i]);
13922         }
13923         this.inPre = oldPre;
13924     },
13925     
13926     
13927     
13928     /**
13929      * compile the template
13930      *
13931      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13932      *
13933      */
13934     compile: function()
13935     {
13936         var s = this.html;
13937         
13938         // covert the html into DOM...
13939         var doc = false;
13940         var div =false;
13941         try {
13942             doc = document.implementation.createHTMLDocument("");
13943             doc.documentElement.innerHTML =   this.html  ;
13944             div = doc.documentElement;
13945         } catch (e) {
13946             // old IE... - nasty -- it causes all sorts of issues.. with
13947             // images getting pulled from server..
13948             div = document.createElement('div');
13949             div.innerHTML = this.html;
13950         }
13951         //doc.documentElement.innerHTML = htmlBody
13952          
13953         
13954         
13955         this.tpls = [];
13956         var _t = this;
13957         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13958         
13959         var tpls = this.tpls;
13960         
13961         // create a top level template from the snippet..
13962         
13963         //Roo.log(div.innerHTML);
13964         
13965         var tpl = {
13966             uid : 'master',
13967             id : this.id++,
13968             attr : false,
13969             value : false,
13970             body : div.innerHTML,
13971             
13972             forCall : false,
13973             execCall : false,
13974             dom : div,
13975             isTop : true
13976             
13977         };
13978         tpls.unshift(tpl);
13979         
13980         
13981         // compile them...
13982         this.tpls = [];
13983         Roo.each(tpls, function(tp){
13984             this.compileTpl(tp);
13985             this.tpls[tp.id] = tp;
13986         }, this);
13987         
13988         this.master = tpls[0];
13989         return this;
13990         
13991         
13992     },
13993     
13994     compileNode : function(node, istop) {
13995         // test for
13996         //Roo.log(node);
13997         
13998         
13999         // skip anything not a tag..
14000         if (node.nodeType != 1) {
14001             if (node.nodeType == 3 && !this.inPre) {
14002                 // reduce white space..
14003                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
14004                 
14005             }
14006             return;
14007         }
14008         
14009         var tpl = {
14010             uid : false,
14011             id : false,
14012             attr : false,
14013             value : false,
14014             body : '',
14015             
14016             forCall : false,
14017             execCall : false,
14018             dom : false,
14019             isTop : istop
14020             
14021             
14022         };
14023         
14024         
14025         switch(true) {
14026             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14027             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14028             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14029             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14030             // no default..
14031         }
14032         
14033         
14034         if (!tpl.attr) {
14035             // just itterate children..
14036             this.iterChild(node,this.compileNode);
14037             return;
14038         }
14039         tpl.uid = this.id++;
14040         tpl.value = node.getAttribute('roo-' +  tpl.attr);
14041         node.removeAttribute('roo-'+ tpl.attr);
14042         if (tpl.attr != 'name') {
14043             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14044             node.parentNode.replaceChild(placeholder,  node);
14045         } else {
14046             
14047             var placeholder =  document.createElement('span');
14048             placeholder.className = 'roo-tpl-' + tpl.value;
14049             node.parentNode.replaceChild(placeholder,  node);
14050         }
14051         
14052         // parent now sees '{domtplXXXX}
14053         this.iterChild(node,this.compileNode);
14054         
14055         // we should now have node body...
14056         var div = document.createElement('div');
14057         div.appendChild(node);
14058         tpl.dom = node;
14059         // this has the unfortunate side effect of converting tagged attributes
14060         // eg. href="{...}" into %7C...%7D
14061         // this has been fixed by searching for those combo's although it's a bit hacky..
14062         
14063         
14064         tpl.body = div.innerHTML;
14065         
14066         
14067          
14068         tpl.id = tpl.uid;
14069         switch(tpl.attr) {
14070             case 'for' :
14071                 switch (tpl.value) {
14072                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14073                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14074                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14075                 }
14076                 break;
14077             
14078             case 'exec':
14079                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14080                 break;
14081             
14082             case 'if':     
14083                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14084                 break;
14085             
14086             case 'name':
14087                 tpl.id  = tpl.value; // replace non characters???
14088                 break;
14089             
14090         }
14091         
14092         
14093         this.tpls.push(tpl);
14094         
14095         
14096         
14097     },
14098     
14099     
14100     
14101     
14102     /**
14103      * Compile a segment of the template into a 'sub-template'
14104      *
14105      * 
14106      * 
14107      *
14108      */
14109     compileTpl : function(tpl)
14110     {
14111         var fm = Roo.util.Format;
14112         var useF = this.disableFormats !== true;
14113         
14114         var sep = Roo.isGecko ? "+\n" : ",\n";
14115         
14116         var undef = function(str) {
14117             Roo.debug && Roo.log("Property not found :"  + str);
14118             return '';
14119         };
14120           
14121         //Roo.log(tpl.body);
14122         
14123         
14124         
14125         var fn = function(m, lbrace, name, format, args)
14126         {
14127             //Roo.log("ARGS");
14128             //Roo.log(arguments);
14129             args = args ? args.replace(/\\'/g,"'") : args;
14130             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14131             if (typeof(format) == 'undefined') {
14132                 format =  'htmlEncode'; 
14133             }
14134             if (format == 'raw' ) {
14135                 format = false;
14136             }
14137             
14138             if(name.substr(0, 6) == 'domtpl'){
14139                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14140             }
14141             
14142             // build an array of options to determine if value is undefined..
14143             
14144             // basically get 'xxxx.yyyy' then do
14145             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14146             //    (function () { Roo.log("Property not found"); return ''; })() :
14147             //    ......
14148             
14149             var udef_ar = [];
14150             var lookfor = '';
14151             Roo.each(name.split('.'), function(st) {
14152                 lookfor += (lookfor.length ? '.': '') + st;
14153                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
14154             });
14155             
14156             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14157             
14158             
14159             if(format && useF){
14160                 
14161                 args = args ? ',' + args : "";
14162                  
14163                 if(format.substr(0, 5) != "this."){
14164                     format = "fm." + format + '(';
14165                 }else{
14166                     format = 'this.call("'+ format.substr(5) + '", ';
14167                     args = ", values";
14168                 }
14169                 
14170                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
14171             }
14172              
14173             if (args && args.length) {
14174                 // called with xxyx.yuu:(test,test)
14175                 // change to ()
14176                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
14177             }
14178             // raw.. - :raw modifier..
14179             return "'"+ sep + udef_st  + name + ")"+sep+"'";
14180             
14181         };
14182         var body;
14183         // branched to use + in gecko and [].join() in others
14184         if(Roo.isGecko){
14185             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
14186                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14187                     "';};};";
14188         }else{
14189             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14190             body.push(tpl.body.replace(/(\r\n|\n)/g,
14191                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14192             body.push("'].join('');};};");
14193             body = body.join('');
14194         }
14195         
14196         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14197        
14198         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14199         eval(body);
14200         
14201         return this;
14202     },
14203      
14204     /**
14205      * same as applyTemplate, except it's done to one of the subTemplates
14206      * when using named templates, you can do:
14207      *
14208      * var str = pl.applySubTemplate('your-name', values);
14209      *
14210      * 
14211      * @param {Number} id of the template
14212      * @param {Object} values to apply to template
14213      * @param {Object} parent (normaly the instance of this object)
14214      */
14215     applySubTemplate : function(id, values, parent)
14216     {
14217         
14218         
14219         var t = this.tpls[id];
14220         
14221         
14222         try { 
14223             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14224                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14225                 return '';
14226             }
14227         } catch(e) {
14228             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14229             Roo.log(values);
14230           
14231             return '';
14232         }
14233         try { 
14234             
14235             if(t.execCall && t.execCall.call(this, values, parent)){
14236                 return '';
14237             }
14238         } catch(e) {
14239             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14240             Roo.log(values);
14241             return '';
14242         }
14243         
14244         try {
14245             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14246             parent = t.target ? values : parent;
14247             if(t.forCall && vs instanceof Array){
14248                 var buf = [];
14249                 for(var i = 0, len = vs.length; i < len; i++){
14250                     try {
14251                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14252                     } catch (e) {
14253                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14254                         Roo.log(e.body);
14255                         //Roo.log(t.compiled);
14256                         Roo.log(vs[i]);
14257                     }   
14258                 }
14259                 return buf.join('');
14260             }
14261         } catch (e) {
14262             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14263             Roo.log(values);
14264             return '';
14265         }
14266         try {
14267             return t.compiled.call(this, vs, parent);
14268         } catch (e) {
14269             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14270             Roo.log(e.body);
14271             //Roo.log(t.compiled);
14272             Roo.log(values);
14273             return '';
14274         }
14275     },
14276
14277    
14278
14279     applyTemplate : function(values){
14280         return this.master.compiled.call(this, values, {});
14281         //var s = this.subs;
14282     },
14283
14284     apply : function(){
14285         return this.applyTemplate.apply(this, arguments);
14286     }
14287
14288  });
14289
14290 Roo.DomTemplate.from = function(el){
14291     el = Roo.getDom(el);
14292     return new Roo.Domtemplate(el.value || el.innerHTML);
14293 };/*
14294  * Based on:
14295  * Ext JS Library 1.1.1
14296  * Copyright(c) 2006-2007, Ext JS, LLC.
14297  *
14298  * Originally Released Under LGPL - original licence link has changed is not relivant.
14299  *
14300  * Fork - LGPL
14301  * <script type="text/javascript">
14302  */
14303
14304 /**
14305  * @class Roo.util.DelayedTask
14306  * Provides a convenient method of performing setTimeout where a new
14307  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14308  * You can use this class to buffer
14309  * the keypress events for a certain number of milliseconds, and perform only if they stop
14310  * for that amount of time.
14311  * @constructor The parameters to this constructor serve as defaults and are not required.
14312  * @param {Function} fn (optional) The default function to timeout
14313  * @param {Object} scope (optional) The default scope of that timeout
14314  * @param {Array} args (optional) The default Array of arguments
14315  */
14316 Roo.util.DelayedTask = function(fn, scope, args){
14317     var id = null, d, t;
14318
14319     var call = function(){
14320         var now = new Date().getTime();
14321         if(now - t >= d){
14322             clearInterval(id);
14323             id = null;
14324             fn.apply(scope, args || []);
14325         }
14326     };
14327     /**
14328      * Cancels any pending timeout and queues a new one
14329      * @param {Number} delay The milliseconds to delay
14330      * @param {Function} newFn (optional) Overrides function passed to constructor
14331      * @param {Object} newScope (optional) Overrides scope passed to constructor
14332      * @param {Array} newArgs (optional) Overrides args passed to constructor
14333      */
14334     this.delay = function(delay, newFn, newScope, newArgs){
14335         if(id && delay != d){
14336             this.cancel();
14337         }
14338         d = delay;
14339         t = new Date().getTime();
14340         fn = newFn || fn;
14341         scope = newScope || scope;
14342         args = newArgs || args;
14343         if(!id){
14344             id = setInterval(call, d);
14345         }
14346     };
14347
14348     /**
14349      * Cancel the last queued timeout
14350      */
14351     this.cancel = function(){
14352         if(id){
14353             clearInterval(id);
14354             id = null;
14355         }
14356     };
14357 };/*
14358  * Based on:
14359  * Ext JS Library 1.1.1
14360  * Copyright(c) 2006-2007, Ext JS, LLC.
14361  *
14362  * Originally Released Under LGPL - original licence link has changed is not relivant.
14363  *
14364  * Fork - LGPL
14365  * <script type="text/javascript">
14366  */
14367 /**
14368  * @class Roo.util.TaskRunner
14369  * Manage background tasks - not sure why this is better that setInterval?
14370  * @static
14371  *
14372  */
14373  
14374 Roo.util.TaskRunner = function(interval){
14375     interval = interval || 10;
14376     var tasks = [], removeQueue = [];
14377     var id = 0;
14378     var running = false;
14379
14380     var stopThread = function(){
14381         running = false;
14382         clearInterval(id);
14383         id = 0;
14384     };
14385
14386     var startThread = function(){
14387         if(!running){
14388             running = true;
14389             id = setInterval(runTasks, interval);
14390         }
14391     };
14392
14393     var removeTask = function(task){
14394         removeQueue.push(task);
14395         if(task.onStop){
14396             task.onStop();
14397         }
14398     };
14399
14400     var runTasks = function(){
14401         if(removeQueue.length > 0){
14402             for(var i = 0, len = removeQueue.length; i < len; i++){
14403                 tasks.remove(removeQueue[i]);
14404             }
14405             removeQueue = [];
14406             if(tasks.length < 1){
14407                 stopThread();
14408                 return;
14409             }
14410         }
14411         var now = new Date().getTime();
14412         for(var i = 0, len = tasks.length; i < len; ++i){
14413             var t = tasks[i];
14414             var itime = now - t.taskRunTime;
14415             if(t.interval <= itime){
14416                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14417                 t.taskRunTime = now;
14418                 if(rt === false || t.taskRunCount === t.repeat){
14419                     removeTask(t);
14420                     return;
14421                 }
14422             }
14423             if(t.duration && t.duration <= (now - t.taskStartTime)){
14424                 removeTask(t);
14425             }
14426         }
14427     };
14428
14429     /**
14430      * Queues a new task.
14431      * @param {Object} task
14432      *
14433      * Task property : interval = how frequent to run.
14434      * Task object should implement
14435      * function run()
14436      * Task object may implement
14437      * function onStop()
14438      */
14439     this.start = function(task){
14440         tasks.push(task);
14441         task.taskStartTime = new Date().getTime();
14442         task.taskRunTime = 0;
14443         task.taskRunCount = 0;
14444         startThread();
14445         return task;
14446     };
14447     /**
14448      * Stop  new task.
14449      * @param {Object} task
14450      */
14451     this.stop = function(task){
14452         removeTask(task);
14453         return task;
14454     };
14455     /**
14456      * Stop all Tasks
14457      */
14458     this.stopAll = function(){
14459         stopThread();
14460         for(var i = 0, len = tasks.length; i < len; i++){
14461             if(tasks[i].onStop){
14462                 tasks[i].onStop();
14463             }
14464         }
14465         tasks = [];
14466         removeQueue = [];
14467     };
14468 };
14469
14470 Roo.TaskMgr = new Roo.util.TaskRunner();/*
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  
14482 /**
14483  * @class Roo.util.MixedCollection
14484  * @extends Roo.util.Observable
14485  * A Collection class that maintains both numeric indexes and keys and exposes events.
14486  * @constructor
14487  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14488  * collection (defaults to false)
14489  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14490  * and return the key value for that item.  This is used when available to look up the key on items that
14491  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14492  * equivalent to providing an implementation for the {@link #getKey} method.
14493  */
14494 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14495     this.items = [];
14496     this.map = {};
14497     this.keys = [];
14498     this.length = 0;
14499     this.addEvents({
14500         /**
14501          * @event clear
14502          * Fires when the collection is cleared.
14503          */
14504         "clear" : true,
14505         /**
14506          * @event add
14507          * Fires when an item is added to the collection.
14508          * @param {Number} index The index at which the item was added.
14509          * @param {Object} o The item added.
14510          * @param {String} key The key associated with the added item.
14511          */
14512         "add" : true,
14513         /**
14514          * @event replace
14515          * Fires when an item is replaced in the collection.
14516          * @param {String} key he key associated with the new added.
14517          * @param {Object} old The item being replaced.
14518          * @param {Object} new The new item.
14519          */
14520         "replace" : true,
14521         /**
14522          * @event remove
14523          * Fires when an item is removed from the collection.
14524          * @param {Object} o The item being removed.
14525          * @param {String} key (optional) The key associated with the removed item.
14526          */
14527         "remove" : true,
14528         "sort" : true
14529     });
14530     this.allowFunctions = allowFunctions === true;
14531     if(keyFn){
14532         this.getKey = keyFn;
14533     }
14534     Roo.util.MixedCollection.superclass.constructor.call(this);
14535 };
14536
14537 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14538     allowFunctions : false,
14539     
14540 /**
14541  * Adds an item to the collection.
14542  * @param {String} key The key to associate with the item
14543  * @param {Object} o The item to add.
14544  * @return {Object} The item added.
14545  */
14546     add : function(key, o){
14547         if(arguments.length == 1){
14548             o = arguments[0];
14549             key = this.getKey(o);
14550         }
14551         if(typeof key == "undefined" || key === null){
14552             this.length++;
14553             this.items.push(o);
14554             this.keys.push(null);
14555         }else{
14556             var old = this.map[key];
14557             if(old){
14558                 return this.replace(key, o);
14559             }
14560             this.length++;
14561             this.items.push(o);
14562             this.map[key] = o;
14563             this.keys.push(key);
14564         }
14565         this.fireEvent("add", this.length-1, o, key);
14566         return o;
14567     },
14568        
14569 /**
14570   * MixedCollection has a generic way to fetch keys if you implement getKey.
14571 <pre><code>
14572 // normal way
14573 var mc = new Roo.util.MixedCollection();
14574 mc.add(someEl.dom.id, someEl);
14575 mc.add(otherEl.dom.id, otherEl);
14576 //and so on
14577
14578 // using getKey
14579 var mc = new Roo.util.MixedCollection();
14580 mc.getKey = function(el){
14581    return el.dom.id;
14582 };
14583 mc.add(someEl);
14584 mc.add(otherEl);
14585
14586 // or via the constructor
14587 var mc = new Roo.util.MixedCollection(false, function(el){
14588    return el.dom.id;
14589 });
14590 mc.add(someEl);
14591 mc.add(otherEl);
14592 </code></pre>
14593  * @param o {Object} The item for which to find the key.
14594  * @return {Object} The key for the passed item.
14595  */
14596     getKey : function(o){
14597          return o.id; 
14598     },
14599    
14600 /**
14601  * Replaces an item in the collection.
14602  * @param {String} key The key associated with the item to replace, or the item to replace.
14603  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14604  * @return {Object}  The new item.
14605  */
14606     replace : function(key, o){
14607         if(arguments.length == 1){
14608             o = arguments[0];
14609             key = this.getKey(o);
14610         }
14611         var old = this.item(key);
14612         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14613              return this.add(key, o);
14614         }
14615         var index = this.indexOfKey(key);
14616         this.items[index] = o;
14617         this.map[key] = o;
14618         this.fireEvent("replace", key, old, o);
14619         return o;
14620     },
14621    
14622 /**
14623  * Adds all elements of an Array or an Object to the collection.
14624  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14625  * an Array of values, each of which are added to the collection.
14626  */
14627     addAll : function(objs){
14628         if(arguments.length > 1 || objs instanceof Array){
14629             var args = arguments.length > 1 ? arguments : objs;
14630             for(var i = 0, len = args.length; i < len; i++){
14631                 this.add(args[i]);
14632             }
14633         }else{
14634             for(var key in objs){
14635                 if(this.allowFunctions || typeof objs[key] != "function"){
14636                     this.add(key, objs[key]);
14637                 }
14638             }
14639         }
14640     },
14641    
14642 /**
14643  * Executes the specified function once for every item in the collection, passing each
14644  * item as the first and only parameter. returning false from the function will stop the iteration.
14645  * @param {Function} fn The function to execute for each item.
14646  * @param {Object} scope (optional) The scope in which to execute the function.
14647  */
14648     each : function(fn, scope){
14649         var items = [].concat(this.items); // each safe for removal
14650         for(var i = 0, len = items.length; i < len; i++){
14651             if(fn.call(scope || items[i], items[i], i, len) === false){
14652                 break;
14653             }
14654         }
14655     },
14656    
14657 /**
14658  * Executes the specified function once for every key in the collection, passing each
14659  * key, and its associated item as the first two parameters.
14660  * @param {Function} fn The function to execute for each item.
14661  * @param {Object} scope (optional) The scope in which to execute the function.
14662  */
14663     eachKey : function(fn, scope){
14664         for(var i = 0, len = this.keys.length; i < len; i++){
14665             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14666         }
14667     },
14668    
14669 /**
14670  * Returns the first item in the collection which elicits a true return value from the
14671  * passed selection function.
14672  * @param {Function} fn The selection function to execute for each item.
14673  * @param {Object} scope (optional) The scope in which to execute the function.
14674  * @return {Object} The first item in the collection which returned true from the selection function.
14675  */
14676     find : function(fn, scope){
14677         for(var i = 0, len = this.items.length; i < len; i++){
14678             if(fn.call(scope || window, this.items[i], this.keys[i])){
14679                 return this.items[i];
14680             }
14681         }
14682         return null;
14683     },
14684    
14685 /**
14686  * Inserts an item at the specified index in the collection.
14687  * @param {Number} index The index to insert the item at.
14688  * @param {String} key The key to associate with the new item, or the item itself.
14689  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14690  * @return {Object} The item inserted.
14691  */
14692     insert : function(index, key, o){
14693         if(arguments.length == 2){
14694             o = arguments[1];
14695             key = this.getKey(o);
14696         }
14697         if(index >= this.length){
14698             return this.add(key, o);
14699         }
14700         this.length++;
14701         this.items.splice(index, 0, o);
14702         if(typeof key != "undefined" && key != null){
14703             this.map[key] = o;
14704         }
14705         this.keys.splice(index, 0, key);
14706         this.fireEvent("add", index, o, key);
14707         return o;
14708     },
14709    
14710 /**
14711  * Removed an item from the collection.
14712  * @param {Object} o The item to remove.
14713  * @return {Object} The item removed.
14714  */
14715     remove : function(o){
14716         return this.removeAt(this.indexOf(o));
14717     },
14718    
14719 /**
14720  * Remove an item from a specified index in the collection.
14721  * @param {Number} index The index within the collection of the item to remove.
14722  */
14723     removeAt : function(index){
14724         if(index < this.length && index >= 0){
14725             this.length--;
14726             var o = this.items[index];
14727             this.items.splice(index, 1);
14728             var key = this.keys[index];
14729             if(typeof key != "undefined"){
14730                 delete this.map[key];
14731             }
14732             this.keys.splice(index, 1);
14733             this.fireEvent("remove", o, key);
14734         }
14735     },
14736    
14737 /**
14738  * Removed an item associated with the passed key fom the collection.
14739  * @param {String} key The key of the item to remove.
14740  */
14741     removeKey : function(key){
14742         return this.removeAt(this.indexOfKey(key));
14743     },
14744    
14745 /**
14746  * Returns the number of items in the collection.
14747  * @return {Number} the number of items in the collection.
14748  */
14749     getCount : function(){
14750         return this.length; 
14751     },
14752    
14753 /**
14754  * Returns index within the collection of the passed Object.
14755  * @param {Object} o The item to find the index of.
14756  * @return {Number} index of the item.
14757  */
14758     indexOf : function(o){
14759         if(!this.items.indexOf){
14760             for(var i = 0, len = this.items.length; i < len; i++){
14761                 if(this.items[i] == o) {
14762                     return i;
14763                 }
14764             }
14765             return -1;
14766         }else{
14767             return this.items.indexOf(o);
14768         }
14769     },
14770    
14771 /**
14772  * Returns index within the collection of the passed key.
14773  * @param {String} key The key to find the index of.
14774  * @return {Number} index of the key.
14775  */
14776     indexOfKey : function(key){
14777         if(!this.keys.indexOf){
14778             for(var i = 0, len = this.keys.length; i < len; i++){
14779                 if(this.keys[i] == key) {
14780                     return i;
14781                 }
14782             }
14783             return -1;
14784         }else{
14785             return this.keys.indexOf(key);
14786         }
14787     },
14788    
14789 /**
14790  * Returns the item associated with the passed key OR index. Key has priority over index.
14791  * @param {String/Number} key The key or index of the item.
14792  * @return {Object} The item associated with the passed key.
14793  */
14794     item : function(key){
14795         if (key === 'length') {
14796             return null;
14797         }
14798         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14799         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14800     },
14801     
14802 /**
14803  * Returns the item at the specified index.
14804  * @param {Number} index The index of the item.
14805  * @return {Object}
14806  */
14807     itemAt : function(index){
14808         return this.items[index];
14809     },
14810     
14811 /**
14812  * Returns the item associated with the passed key.
14813  * @param {String/Number} key The key of the item.
14814  * @return {Object} The item associated with the passed key.
14815  */
14816     key : function(key){
14817         return this.map[key];
14818     },
14819    
14820 /**
14821  * Returns true if the collection contains the passed Object as an item.
14822  * @param {Object} o  The Object to look for in the collection.
14823  * @return {Boolean} True if the collection contains the Object as an item.
14824  */
14825     contains : function(o){
14826         return this.indexOf(o) != -1;
14827     },
14828    
14829 /**
14830  * Returns true if the collection contains the passed Object as a key.
14831  * @param {String} key The key to look for in the collection.
14832  * @return {Boolean} True if the collection contains the Object as a key.
14833  */
14834     containsKey : function(key){
14835         return typeof this.map[key] != "undefined";
14836     },
14837    
14838 /**
14839  * Removes all items from the collection.
14840  */
14841     clear : function(){
14842         this.length = 0;
14843         this.items = [];
14844         this.keys = [];
14845         this.map = {};
14846         this.fireEvent("clear");
14847     },
14848    
14849 /**
14850  * Returns the first item in the collection.
14851  * @return {Object} the first item in the collection..
14852  */
14853     first : function(){
14854         return this.items[0]; 
14855     },
14856    
14857 /**
14858  * Returns the last item in the collection.
14859  * @return {Object} the last item in the collection..
14860  */
14861     last : function(){
14862         return this.items[this.length-1];   
14863     },
14864     
14865     _sort : function(property, dir, fn){
14866         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14867         fn = fn || function(a, b){
14868             return a-b;
14869         };
14870         var c = [], k = this.keys, items = this.items;
14871         for(var i = 0, len = items.length; i < len; i++){
14872             c[c.length] = {key: k[i], value: items[i], index: i};
14873         }
14874         c.sort(function(a, b){
14875             var v = fn(a[property], b[property]) * dsc;
14876             if(v == 0){
14877                 v = (a.index < b.index ? -1 : 1);
14878             }
14879             return v;
14880         });
14881         for(var i = 0, len = c.length; i < len; i++){
14882             items[i] = c[i].value;
14883             k[i] = c[i].key;
14884         }
14885         this.fireEvent("sort", this);
14886     },
14887     
14888     /**
14889      * Sorts this collection with the passed comparison function
14890      * @param {String} direction (optional) "ASC" or "DESC"
14891      * @param {Function} fn (optional) comparison function
14892      */
14893     sort : function(dir, fn){
14894         this._sort("value", dir, fn);
14895     },
14896     
14897     /**
14898      * Sorts this collection by keys
14899      * @param {String} direction (optional) "ASC" or "DESC"
14900      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14901      */
14902     keySort : function(dir, fn){
14903         this._sort("key", dir, fn || function(a, b){
14904             return String(a).toUpperCase()-String(b).toUpperCase();
14905         });
14906     },
14907     
14908     /**
14909      * Returns a range of items in this collection
14910      * @param {Number} startIndex (optional) defaults to 0
14911      * @param {Number} endIndex (optional) default to the last item
14912      * @return {Array} An array of items
14913      */
14914     getRange : function(start, end){
14915         var items = this.items;
14916         if(items.length < 1){
14917             return [];
14918         }
14919         start = start || 0;
14920         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14921         var r = [];
14922         if(start <= end){
14923             for(var i = start; i <= end; i++) {
14924                     r[r.length] = items[i];
14925             }
14926         }else{
14927             for(var i = start; i >= end; i--) {
14928                     r[r.length] = items[i];
14929             }
14930         }
14931         return r;
14932     },
14933         
14934     /**
14935      * Filter the <i>objects</i> in this collection by a specific property. 
14936      * Returns a new collection that has been filtered.
14937      * @param {String} property A property on your objects
14938      * @param {String/RegExp} value Either string that the property values 
14939      * should start with or a RegExp to test against the property
14940      * @return {MixedCollection} The new filtered collection
14941      */
14942     filter : function(property, value){
14943         if(!value.exec){ // not a regex
14944             value = String(value);
14945             if(value.length == 0){
14946                 return this.clone();
14947             }
14948             value = new RegExp("^" + Roo.escapeRe(value), "i");
14949         }
14950         return this.filterBy(function(o){
14951             return o && value.test(o[property]);
14952         });
14953         },
14954     
14955     /**
14956      * Filter by a function. * Returns a new collection that has been filtered.
14957      * The passed function will be called with each 
14958      * object in the collection. If the function returns true, the value is included 
14959      * otherwise it is filtered.
14960      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14961      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14962      * @return {MixedCollection} The new filtered collection
14963      */
14964     filterBy : function(fn, scope){
14965         var r = new Roo.util.MixedCollection();
14966         r.getKey = this.getKey;
14967         var k = this.keys, it = this.items;
14968         for(var i = 0, len = it.length; i < len; i++){
14969             if(fn.call(scope||this, it[i], k[i])){
14970                                 r.add(k[i], it[i]);
14971                         }
14972         }
14973         return r;
14974     },
14975     
14976     /**
14977      * Creates a duplicate of this collection
14978      * @return {MixedCollection}
14979      */
14980     clone : function(){
14981         var r = new Roo.util.MixedCollection();
14982         var k = this.keys, it = this.items;
14983         for(var i = 0, len = it.length; i < len; i++){
14984             r.add(k[i], it[i]);
14985         }
14986         r.getKey = this.getKey;
14987         return r;
14988     }
14989 });
14990 /**
14991  * Returns the item associated with the passed key or index.
14992  * @method
14993  * @param {String/Number} key The key or index of the item.
14994  * @return {Object} The item associated with the passed key.
14995  */
14996 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14997  * Based on:
14998  * Ext JS Library 1.1.1
14999  * Copyright(c) 2006-2007, Ext JS, LLC.
15000  *
15001  * Originally Released Under LGPL - original licence link has changed is not relivant.
15002  *
15003  * Fork - LGPL
15004  * <script type="text/javascript">
15005  */
15006 /**
15007  * @class Roo.util.JSON
15008  * Modified version of Douglas Crockford"s json.js that doesn"t
15009  * mess with the Object prototype 
15010  * http://www.json.org/js.html
15011  * @static
15012  */
15013 Roo.util.JSON = new (function(){
15014     var useHasOwn = {}.hasOwnProperty ? true : false;
15015     
15016     // crashes Safari in some instances
15017     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15018     
15019     var pad = function(n) {
15020         return n < 10 ? "0" + n : n;
15021     };
15022     
15023     var m = {
15024         "\b": '\\b',
15025         "\t": '\\t',
15026         "\n": '\\n',
15027         "\f": '\\f',
15028         "\r": '\\r',
15029         '"' : '\\"',
15030         "\\": '\\\\'
15031     };
15032
15033     var encodeString = function(s){
15034         if (/["\\\x00-\x1f]/.test(s)) {
15035             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15036                 var c = m[b];
15037                 if(c){
15038                     return c;
15039                 }
15040                 c = b.charCodeAt();
15041                 return "\\u00" +
15042                     Math.floor(c / 16).toString(16) +
15043                     (c % 16).toString(16);
15044             }) + '"';
15045         }
15046         return '"' + s + '"';
15047     };
15048     
15049     var encodeArray = function(o){
15050         var a = ["["], b, i, l = o.length, v;
15051             for (i = 0; i < l; i += 1) {
15052                 v = o[i];
15053                 switch (typeof v) {
15054                     case "undefined":
15055                     case "function":
15056                     case "unknown":
15057                         break;
15058                     default:
15059                         if (b) {
15060                             a.push(',');
15061                         }
15062                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15063                         b = true;
15064                 }
15065             }
15066             a.push("]");
15067             return a.join("");
15068     };
15069     
15070     var encodeDate = function(o){
15071         return '"' + o.getFullYear() + "-" +
15072                 pad(o.getMonth() + 1) + "-" +
15073                 pad(o.getDate()) + "T" +
15074                 pad(o.getHours()) + ":" +
15075                 pad(o.getMinutes()) + ":" +
15076                 pad(o.getSeconds()) + '"';
15077     };
15078     
15079     /**
15080      * Encodes an Object, Array or other value
15081      * @param {Mixed} o The variable to encode
15082      * @return {String} The JSON string
15083      */
15084     this.encode = function(o)
15085     {
15086         // should this be extended to fully wrap stringify..
15087         
15088         if(typeof o == "undefined" || o === null){
15089             return "null";
15090         }else if(o instanceof Array){
15091             return encodeArray(o);
15092         }else if(o instanceof Date){
15093             return encodeDate(o);
15094         }else if(typeof o == "string"){
15095             return encodeString(o);
15096         }else if(typeof o == "number"){
15097             return isFinite(o) ? String(o) : "null";
15098         }else if(typeof o == "boolean"){
15099             return String(o);
15100         }else {
15101             var a = ["{"], b, i, v;
15102             for (i in o) {
15103                 if(!useHasOwn || o.hasOwnProperty(i)) {
15104                     v = o[i];
15105                     switch (typeof v) {
15106                     case "undefined":
15107                     case "function":
15108                     case "unknown":
15109                         break;
15110                     default:
15111                         if(b){
15112                             a.push(',');
15113                         }
15114                         a.push(this.encode(i), ":",
15115                                 v === null ? "null" : this.encode(v));
15116                         b = true;
15117                     }
15118                 }
15119             }
15120             a.push("}");
15121             return a.join("");
15122         }
15123     };
15124     
15125     /**
15126      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15127      * @param {String} json The JSON string
15128      * @return {Object} The resulting object
15129      */
15130     this.decode = function(json){
15131         
15132         return  /** eval:var:json */ eval("(" + json + ')');
15133     };
15134 })();
15135 /** 
15136  * Shorthand for {@link Roo.util.JSON#encode}
15137  * @member Roo encode 
15138  * @method */
15139 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15140 /** 
15141  * Shorthand for {@link Roo.util.JSON#decode}
15142  * @member Roo decode 
15143  * @method */
15144 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15145 /*
15146  * Based on:
15147  * Ext JS Library 1.1.1
15148  * Copyright(c) 2006-2007, Ext JS, LLC.
15149  *
15150  * Originally Released Under LGPL - original licence link has changed is not relivant.
15151  *
15152  * Fork - LGPL
15153  * <script type="text/javascript">
15154  */
15155  
15156 /**
15157  * @class Roo.util.Format
15158  * Reusable data formatting functions
15159  * @static
15160  */
15161 Roo.util.Format = function(){
15162     var trimRe = /^\s+|\s+$/g;
15163     return {
15164         /**
15165          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15166          * @param {String} value The string to truncate
15167          * @param {Number} length The maximum length to allow before truncating
15168          * @return {String} The converted text
15169          */
15170         ellipsis : function(value, len){
15171             if(value && value.length > len){
15172                 return value.substr(0, len-3)+"...";
15173             }
15174             return value;
15175         },
15176
15177         /**
15178          * Checks a reference and converts it to empty string if it is undefined
15179          * @param {Mixed} value Reference to check
15180          * @return {Mixed} Empty string if converted, otherwise the original value
15181          */
15182         undef : function(value){
15183             return typeof value != "undefined" ? value : "";
15184         },
15185
15186         /**
15187          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15188          * @param {String} value The string to encode
15189          * @return {String} The encoded text
15190          */
15191         htmlEncode : function(value){
15192             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15193         },
15194
15195         /**
15196          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15197          * @param {String} value The string to decode
15198          * @return {String} The decoded text
15199          */
15200         htmlDecode : function(value){
15201             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15202         },
15203
15204         /**
15205          * Trims any whitespace from either side of a string
15206          * @param {String} value The text to trim
15207          * @return {String} The trimmed text
15208          */
15209         trim : function(value){
15210             return String(value).replace(trimRe, "");
15211         },
15212
15213         /**
15214          * Returns a substring from within an original string
15215          * @param {String} value The original text
15216          * @param {Number} start The start index of the substring
15217          * @param {Number} length The length of the substring
15218          * @return {String} The substring
15219          */
15220         substr : function(value, start, length){
15221             return String(value).substr(start, length);
15222         },
15223
15224         /**
15225          * Converts a string to all lower case letters
15226          * @param {String} value The text to convert
15227          * @return {String} The converted text
15228          */
15229         lowercase : function(value){
15230             return String(value).toLowerCase();
15231         },
15232
15233         /**
15234          * Converts a string to all upper case letters
15235          * @param {String} value The text to convert
15236          * @return {String} The converted text
15237          */
15238         uppercase : function(value){
15239             return String(value).toUpperCase();
15240         },
15241
15242         /**
15243          * Converts the first character only of a string to upper case
15244          * @param {String} value The text to convert
15245          * @return {String} The converted text
15246          */
15247         capitalize : function(value){
15248             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15249         },
15250
15251         // private
15252         call : function(value, fn){
15253             if(arguments.length > 2){
15254                 var args = Array.prototype.slice.call(arguments, 2);
15255                 args.unshift(value);
15256                  
15257                 return /** eval:var:value */  eval(fn).apply(window, args);
15258             }else{
15259                 /** eval:var:value */
15260                 return /** eval:var:value */ eval(fn).call(window, value);
15261             }
15262         },
15263
15264        
15265         /**
15266          * safer version of Math.toFixed..??/
15267          * @param {Number/String} value The numeric value to format
15268          * @param {Number/String} value Decimal places 
15269          * @return {String} The formatted currency string
15270          */
15271         toFixed : function(v, n)
15272         {
15273             // why not use to fixed - precision is buggered???
15274             if (!n) {
15275                 return Math.round(v-0);
15276             }
15277             var fact = Math.pow(10,n+1);
15278             v = (Math.round((v-0)*fact))/fact;
15279             var z = (''+fact).substring(2);
15280             if (v == Math.floor(v)) {
15281                 return Math.floor(v) + '.' + z;
15282             }
15283             
15284             // now just padd decimals..
15285             var ps = String(v).split('.');
15286             var fd = (ps[1] + z);
15287             var r = fd.substring(0,n); 
15288             var rm = fd.substring(n); 
15289             if (rm < 5) {
15290                 return ps[0] + '.' + r;
15291             }
15292             r*=1; // turn it into a number;
15293             r++;
15294             if (String(r).length != n) {
15295                 ps[0]*=1;
15296                 ps[0]++;
15297                 r = String(r).substring(1); // chop the end off.
15298             }
15299             
15300             return ps[0] + '.' + r;
15301              
15302         },
15303         
15304         /**
15305          * Format a number as US currency
15306          * @param {Number/String} value The numeric value to format
15307          * @return {String} The formatted currency string
15308          */
15309         usMoney : function(v){
15310             return '$' + Roo.util.Format.number(v);
15311         },
15312         
15313         /**
15314          * Format a number
15315          * eventually this should probably emulate php's number_format
15316          * @param {Number/String} value The numeric value to format
15317          * @param {Number} decimals number of decimal places
15318          * @param {String} delimiter for thousands (default comma)
15319          * @return {String} The formatted currency string
15320          */
15321         number : function(v, decimals, thousandsDelimiter)
15322         {
15323             // multiply and round.
15324             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15325             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15326             
15327             var mul = Math.pow(10, decimals);
15328             var zero = String(mul).substring(1);
15329             v = (Math.round((v-0)*mul))/mul;
15330             
15331             // if it's '0' number.. then
15332             
15333             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15334             v = String(v);
15335             var ps = v.split('.');
15336             var whole = ps[0];
15337             
15338             var r = /(\d+)(\d{3})/;
15339             // add comma's
15340             
15341             if(thousandsDelimiter.length != 0) {
15342                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15343             } 
15344             
15345             var sub = ps[1] ?
15346                     // has decimals..
15347                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15348                     // does not have decimals
15349                     (decimals ? ('.' + zero) : '');
15350             
15351             
15352             return whole + sub ;
15353         },
15354         
15355         /**
15356          * Parse a value into a formatted date using the specified format pattern.
15357          * @param {Mixed} value The value to format
15358          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15359          * @return {String} The formatted date string
15360          */
15361         date : function(v, format){
15362             if(!v){
15363                 return "";
15364             }
15365             if(!(v instanceof Date)){
15366                 v = new Date(Date.parse(v));
15367             }
15368             return v.dateFormat(format || Roo.util.Format.defaults.date);
15369         },
15370
15371         /**
15372          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15373          * @param {String} format Any valid date format string
15374          * @return {Function} The date formatting function
15375          */
15376         dateRenderer : function(format){
15377             return function(v){
15378                 return Roo.util.Format.date(v, format);  
15379             };
15380         },
15381
15382         // private
15383         stripTagsRE : /<\/?[^>]+>/gi,
15384         
15385         /**
15386          * Strips all HTML tags
15387          * @param {Mixed} value The text from which to strip tags
15388          * @return {String} The stripped text
15389          */
15390         stripTags : function(v){
15391             return !v ? v : String(v).replace(this.stripTagsRE, "");
15392         },
15393         
15394         /**
15395          * Size in Mb,Gb etc.
15396          * @param {Number} value The number to be formated
15397          * @param {number} decimals how many decimal places
15398          * @return {String} the formated string
15399          */
15400         size : function(value, decimals)
15401         {
15402             var sizes = ['b', 'k', 'M', 'G', 'T'];
15403             if (value == 0) {
15404                 return 0;
15405             }
15406             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15407             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15408         }
15409         
15410         
15411         
15412     };
15413 }();
15414 Roo.util.Format.defaults = {
15415     date : 'd/M/Y'
15416 };/*
15417  * Based on:
15418  * Ext JS Library 1.1.1
15419  * Copyright(c) 2006-2007, Ext JS, LLC.
15420  *
15421  * Originally Released Under LGPL - original licence link has changed is not relivant.
15422  *
15423  * Fork - LGPL
15424  * <script type="text/javascript">
15425  */
15426
15427
15428  
15429
15430 /**
15431  * @class Roo.MasterTemplate
15432  * @extends Roo.Template
15433  * Provides a template that can have child templates. The syntax is:
15434 <pre><code>
15435 var t = new Roo.MasterTemplate(
15436         '&lt;select name="{name}"&gt;',
15437                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15438         '&lt;/select&gt;'
15439 );
15440 t.add('options', {value: 'foo', text: 'bar'});
15441 // or you can add multiple child elements in one shot
15442 t.addAll('options', [
15443     {value: 'foo', text: 'bar'},
15444     {value: 'foo2', text: 'bar2'},
15445     {value: 'foo3', text: 'bar3'}
15446 ]);
15447 // then append, applying the master template values
15448 t.append('my-form', {name: 'my-select'});
15449 </code></pre>
15450 * A name attribute for the child template is not required if you have only one child
15451 * template or you want to refer to them by index.
15452  */
15453 Roo.MasterTemplate = function(){
15454     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15455     this.originalHtml = this.html;
15456     var st = {};
15457     var m, re = this.subTemplateRe;
15458     re.lastIndex = 0;
15459     var subIndex = 0;
15460     while(m = re.exec(this.html)){
15461         var name = m[1], content = m[2];
15462         st[subIndex] = {
15463             name: name,
15464             index: subIndex,
15465             buffer: [],
15466             tpl : new Roo.Template(content)
15467         };
15468         if(name){
15469             st[name] = st[subIndex];
15470         }
15471         st[subIndex].tpl.compile();
15472         st[subIndex].tpl.call = this.call.createDelegate(this);
15473         subIndex++;
15474     }
15475     this.subCount = subIndex;
15476     this.subs = st;
15477 };
15478 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15479     /**
15480     * The regular expression used to match sub templates
15481     * @type RegExp
15482     * @property
15483     */
15484     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15485
15486     /**
15487      * Applies the passed values to a child template.
15488      * @param {String/Number} name (optional) The name or index of the child template
15489      * @param {Array/Object} values The values to be applied to the template
15490      * @return {MasterTemplate} this
15491      */
15492      add : function(name, values){
15493         if(arguments.length == 1){
15494             values = arguments[0];
15495             name = 0;
15496         }
15497         var s = this.subs[name];
15498         s.buffer[s.buffer.length] = s.tpl.apply(values);
15499         return this;
15500     },
15501
15502     /**
15503      * Applies all the passed values to a child template.
15504      * @param {String/Number} name (optional) The name or index of the child template
15505      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15506      * @param {Boolean} reset (optional) True to reset the template first
15507      * @return {MasterTemplate} this
15508      */
15509     fill : function(name, values, reset){
15510         var a = arguments;
15511         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15512             values = a[0];
15513             name = 0;
15514             reset = a[1];
15515         }
15516         if(reset){
15517             this.reset();
15518         }
15519         for(var i = 0, len = values.length; i < len; i++){
15520             this.add(name, values[i]);
15521         }
15522         return this;
15523     },
15524
15525     /**
15526      * Resets the template for reuse
15527      * @return {MasterTemplate} this
15528      */
15529      reset : function(){
15530         var s = this.subs;
15531         for(var i = 0; i < this.subCount; i++){
15532             s[i].buffer = [];
15533         }
15534         return this;
15535     },
15536
15537     applyTemplate : function(values){
15538         var s = this.subs;
15539         var replaceIndex = -1;
15540         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15541             return s[++replaceIndex].buffer.join("");
15542         });
15543         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15544     },
15545
15546     apply : function(){
15547         return this.applyTemplate.apply(this, arguments);
15548     },
15549
15550     compile : function(){return this;}
15551 });
15552
15553 /**
15554  * Alias for fill().
15555  * @method
15556  */
15557 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15558  /**
15559  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15560  * var tpl = Roo.MasterTemplate.from('element-id');
15561  * @param {String/HTMLElement} el
15562  * @param {Object} config
15563  * @static
15564  */
15565 Roo.MasterTemplate.from = function(el, config){
15566     el = Roo.getDom(el);
15567     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15568 };/*
15569  * Based on:
15570  * Ext JS Library 1.1.1
15571  * Copyright(c) 2006-2007, Ext JS, LLC.
15572  *
15573  * Originally Released Under LGPL - original licence link has changed is not relivant.
15574  *
15575  * Fork - LGPL
15576  * <script type="text/javascript">
15577  */
15578
15579  
15580 /**
15581  * @class Roo.util.CSS
15582  * Utility class for manipulating CSS rules
15583  * @static
15584
15585  */
15586 Roo.util.CSS = function(){
15587         var rules = null;
15588         var doc = document;
15589
15590     var camelRe = /(-[a-z])/gi;
15591     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15592
15593    return {
15594    /**
15595     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15596     * tag and appended to the HEAD of the document.
15597     * @param {String|Object} cssText The text containing the css rules
15598     * @param {String} id An id to add to the stylesheet for later removal
15599     * @return {StyleSheet}
15600     */
15601     createStyleSheet : function(cssText, id){
15602         var ss;
15603         var head = doc.getElementsByTagName("head")[0];
15604         var nrules = doc.createElement("style");
15605         nrules.setAttribute("type", "text/css");
15606         if(id){
15607             nrules.setAttribute("id", id);
15608         }
15609         if (typeof(cssText) != 'string') {
15610             // support object maps..
15611             // not sure if this a good idea.. 
15612             // perhaps it should be merged with the general css handling
15613             // and handle js style props.
15614             var cssTextNew = [];
15615             for(var n in cssText) {
15616                 var citems = [];
15617                 for(var k in cssText[n]) {
15618                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15619                 }
15620                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15621                 
15622             }
15623             cssText = cssTextNew.join("\n");
15624             
15625         }
15626        
15627        
15628        if(Roo.isIE){
15629            head.appendChild(nrules);
15630            ss = nrules.styleSheet;
15631            ss.cssText = cssText;
15632        }else{
15633            try{
15634                 nrules.appendChild(doc.createTextNode(cssText));
15635            }catch(e){
15636                nrules.cssText = cssText; 
15637            }
15638            head.appendChild(nrules);
15639            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15640        }
15641        this.cacheStyleSheet(ss);
15642        return ss;
15643    },
15644
15645    /**
15646     * Removes a style or link tag by id
15647     * @param {String} id The id of the tag
15648     */
15649    removeStyleSheet : function(id){
15650        var existing = doc.getElementById(id);
15651        if(existing){
15652            existing.parentNode.removeChild(existing);
15653        }
15654    },
15655
15656    /**
15657     * Dynamically swaps an existing stylesheet reference for a new one
15658     * @param {String} id The id of an existing link tag to remove
15659     * @param {String} url The href of the new stylesheet to include
15660     */
15661    swapStyleSheet : function(id, url){
15662        this.removeStyleSheet(id);
15663        var ss = doc.createElement("link");
15664        ss.setAttribute("rel", "stylesheet");
15665        ss.setAttribute("type", "text/css");
15666        ss.setAttribute("id", id);
15667        ss.setAttribute("href", url);
15668        doc.getElementsByTagName("head")[0].appendChild(ss);
15669    },
15670    
15671    /**
15672     * Refresh the rule cache if you have dynamically added stylesheets
15673     * @return {Object} An object (hash) of rules indexed by selector
15674     */
15675    refreshCache : function(){
15676        return this.getRules(true);
15677    },
15678
15679    // private
15680    cacheStyleSheet : function(stylesheet){
15681        if(!rules){
15682            rules = {};
15683        }
15684        try{// try catch for cross domain access issue
15685            var ssRules = stylesheet.cssRules || stylesheet.rules;
15686            for(var j = ssRules.length-1; j >= 0; --j){
15687                rules[ssRules[j].selectorText] = ssRules[j];
15688            }
15689        }catch(e){}
15690    },
15691    
15692    /**
15693     * Gets all css rules for the document
15694     * @param {Boolean} refreshCache true to refresh the internal cache
15695     * @return {Object} An object (hash) of rules indexed by selector
15696     */
15697    getRules : function(refreshCache){
15698                 if(rules == null || refreshCache){
15699                         rules = {};
15700                         var ds = doc.styleSheets;
15701                         for(var i =0, len = ds.length; i < len; i++){
15702                             try{
15703                         this.cacheStyleSheet(ds[i]);
15704                     }catch(e){} 
15705                 }
15706                 }
15707                 return rules;
15708         },
15709         
15710         /**
15711     * Gets an an individual CSS rule by selector(s)
15712     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15713     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15714     * @return {CSSRule} The CSS rule or null if one is not found
15715     */
15716    getRule : function(selector, refreshCache){
15717                 var rs = this.getRules(refreshCache);
15718                 if(!(selector instanceof Array)){
15719                     return rs[selector];
15720                 }
15721                 for(var i = 0; i < selector.length; i++){
15722                         if(rs[selector[i]]){
15723                                 return rs[selector[i]];
15724                         }
15725                 }
15726                 return null;
15727         },
15728         
15729         
15730         /**
15731     * Updates a rule property
15732     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15733     * @param {String} property The css property
15734     * @param {String} value The new value for the property
15735     * @return {Boolean} true If a rule was found and updated
15736     */
15737    updateRule : function(selector, property, value){
15738                 if(!(selector instanceof Array)){
15739                         var rule = this.getRule(selector);
15740                         if(rule){
15741                                 rule.style[property.replace(camelRe, camelFn)] = value;
15742                                 return true;
15743                         }
15744                 }else{
15745                         for(var i = 0; i < selector.length; i++){
15746                                 if(this.updateRule(selector[i], property, value)){
15747                                         return true;
15748                                 }
15749                         }
15750                 }
15751                 return false;
15752         }
15753    };   
15754 }();/*
15755  * Based on:
15756  * Ext JS Library 1.1.1
15757  * Copyright(c) 2006-2007, Ext JS, LLC.
15758  *
15759  * Originally Released Under LGPL - original licence link has changed is not relivant.
15760  *
15761  * Fork - LGPL
15762  * <script type="text/javascript">
15763  */
15764
15765  
15766
15767 /**
15768  * @class Roo.util.ClickRepeater
15769  * @extends Roo.util.Observable
15770  * 
15771  * A wrapper class which can be applied to any element. Fires a "click" event while the
15772  * mouse is pressed. The interval between firings may be specified in the config but
15773  * defaults to 10 milliseconds.
15774  * 
15775  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15776  * 
15777  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15778  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15779  * Similar to an autorepeat key delay.
15780  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15781  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15782  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15783  *           "interval" and "delay" are ignored. "immediate" is honored.
15784  * @cfg {Boolean} preventDefault True to prevent the default click event
15785  * @cfg {Boolean} stopDefault True to stop the default click event
15786  * 
15787  * @history
15788  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15789  *     2007-02-02 jvs Renamed to ClickRepeater
15790  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15791  *
15792  *  @constructor
15793  * @param {String/HTMLElement/Element} el The element to listen on
15794  * @param {Object} config
15795  **/
15796 Roo.util.ClickRepeater = function(el, config)
15797 {
15798     this.el = Roo.get(el);
15799     this.el.unselectable();
15800
15801     Roo.apply(this, config);
15802
15803     this.addEvents({
15804     /**
15805      * @event mousedown
15806      * Fires when the mouse button is depressed.
15807      * @param {Roo.util.ClickRepeater} this
15808      */
15809         "mousedown" : true,
15810     /**
15811      * @event click
15812      * Fires on a specified interval during the time the element is pressed.
15813      * @param {Roo.util.ClickRepeater} this
15814      */
15815         "click" : true,
15816     /**
15817      * @event mouseup
15818      * Fires when the mouse key is released.
15819      * @param {Roo.util.ClickRepeater} this
15820      */
15821         "mouseup" : true
15822     });
15823
15824     this.el.on("mousedown", this.handleMouseDown, this);
15825     if(this.preventDefault || this.stopDefault){
15826         this.el.on("click", function(e){
15827             if(this.preventDefault){
15828                 e.preventDefault();
15829             }
15830             if(this.stopDefault){
15831                 e.stopEvent();
15832             }
15833         }, this);
15834     }
15835
15836     // allow inline handler
15837     if(this.handler){
15838         this.on("click", this.handler,  this.scope || this);
15839     }
15840
15841     Roo.util.ClickRepeater.superclass.constructor.call(this);
15842 };
15843
15844 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15845     interval : 20,
15846     delay: 250,
15847     preventDefault : true,
15848     stopDefault : false,
15849     timer : 0,
15850
15851     // private
15852     handleMouseDown : function(){
15853         clearTimeout(this.timer);
15854         this.el.blur();
15855         if(this.pressClass){
15856             this.el.addClass(this.pressClass);
15857         }
15858         this.mousedownTime = new Date();
15859
15860         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15861         this.el.on("mouseout", this.handleMouseOut, this);
15862
15863         this.fireEvent("mousedown", this);
15864         this.fireEvent("click", this);
15865         
15866         this.timer = this.click.defer(this.delay || this.interval, this);
15867     },
15868
15869     // private
15870     click : function(){
15871         this.fireEvent("click", this);
15872         this.timer = this.click.defer(this.getInterval(), this);
15873     },
15874
15875     // private
15876     getInterval: function(){
15877         if(!this.accelerate){
15878             return this.interval;
15879         }
15880         var pressTime = this.mousedownTime.getElapsed();
15881         if(pressTime < 500){
15882             return 400;
15883         }else if(pressTime < 1700){
15884             return 320;
15885         }else if(pressTime < 2600){
15886             return 250;
15887         }else if(pressTime < 3500){
15888             return 180;
15889         }else if(pressTime < 4400){
15890             return 140;
15891         }else if(pressTime < 5300){
15892             return 80;
15893         }else if(pressTime < 6200){
15894             return 50;
15895         }else{
15896             return 10;
15897         }
15898     },
15899
15900     // private
15901     handleMouseOut : function(){
15902         clearTimeout(this.timer);
15903         if(this.pressClass){
15904             this.el.removeClass(this.pressClass);
15905         }
15906         this.el.on("mouseover", this.handleMouseReturn, this);
15907     },
15908
15909     // private
15910     handleMouseReturn : function(){
15911         this.el.un("mouseover", this.handleMouseReturn);
15912         if(this.pressClass){
15913             this.el.addClass(this.pressClass);
15914         }
15915         this.click();
15916     },
15917
15918     // private
15919     handleMouseUp : function(){
15920         clearTimeout(this.timer);
15921         this.el.un("mouseover", this.handleMouseReturn);
15922         this.el.un("mouseout", this.handleMouseOut);
15923         Roo.get(document).un("mouseup", this.handleMouseUp);
15924         this.el.removeClass(this.pressClass);
15925         this.fireEvent("mouseup", this);
15926     }
15927 });/**
15928  * @class Roo.util.Clipboard
15929  * @static
15930  * 
15931  * Clipboard UTILS
15932  * 
15933  **/
15934 Roo.util.Clipboard = {
15935     /**
15936      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15937      * @param {String} text to copy to clipboard
15938      */
15939     write : function(text) {
15940         // navigator clipboard api needs a secure context (https)
15941         if (navigator.clipboard && window.isSecureContext) {
15942             // navigator clipboard api method'
15943             navigator.clipboard.writeText(text);
15944             return ;
15945         } 
15946         // text area method
15947         var ta = document.createElement("textarea");
15948         ta.value = text;
15949         // make the textarea out of viewport
15950         ta.style.position = "fixed";
15951         ta.style.left = "-999999px";
15952         ta.style.top = "-999999px";
15953         document.body.appendChild(ta);
15954         ta.focus();
15955         ta.select();
15956         document.execCommand('copy');
15957         (function() {
15958             ta.remove();
15959         }).defer(100);
15960         
15961     }
15962         
15963 }
15964     /*
15965  * Based on:
15966  * Ext JS Library 1.1.1
15967  * Copyright(c) 2006-2007, Ext JS, LLC.
15968  *
15969  * Originally Released Under LGPL - original licence link has changed is not relivant.
15970  *
15971  * Fork - LGPL
15972  * <script type="text/javascript">
15973  */
15974
15975  
15976 /**
15977  * @class Roo.KeyNav
15978  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15979  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15980  * way to implement custom navigation schemes for any UI component.</p>
15981  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15982  * pageUp, pageDown, del, home, end.  Usage:</p>
15983  <pre><code>
15984 var nav = new Roo.KeyNav("my-element", {
15985     "left" : function(e){
15986         this.moveLeft(e.ctrlKey);
15987     },
15988     "right" : function(e){
15989         this.moveRight(e.ctrlKey);
15990     },
15991     "enter" : function(e){
15992         this.save();
15993     },
15994     scope : this
15995 });
15996 </code></pre>
15997  * @constructor
15998  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15999  * @param {Object} config The config
16000  */
16001 Roo.KeyNav = function(el, config){
16002     this.el = Roo.get(el);
16003     Roo.apply(this, config);
16004     if(!this.disabled){
16005         this.disabled = true;
16006         this.enable();
16007     }
16008 };
16009
16010 Roo.KeyNav.prototype = {
16011     /**
16012      * @cfg {Boolean} disabled
16013      * True to disable this KeyNav instance (defaults to false)
16014      */
16015     disabled : false,
16016     /**
16017      * @cfg {String} defaultEventAction
16018      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
16019      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16020      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16021      */
16022     defaultEventAction: "stopEvent",
16023     /**
16024      * @cfg {Boolean} forceKeyDown
16025      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
16026      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16027      * handle keydown instead of keypress.
16028      */
16029     forceKeyDown : false,
16030
16031     // private
16032     prepareEvent : function(e){
16033         var k = e.getKey();
16034         var h = this.keyToHandler[k];
16035         //if(h && this[h]){
16036         //    e.stopPropagation();
16037         //}
16038         if(Roo.isSafari && h && k >= 37 && k <= 40){
16039             e.stopEvent();
16040         }
16041     },
16042
16043     // private
16044     relay : function(e){
16045         var k = e.getKey();
16046         var h = this.keyToHandler[k];
16047         if(h && this[h]){
16048             if(this.doRelay(e, this[h], h) !== true){
16049                 e[this.defaultEventAction]();
16050             }
16051         }
16052     },
16053
16054     // private
16055     doRelay : function(e, h, hname){
16056         return h.call(this.scope || this, e);
16057     },
16058
16059     // possible handlers
16060     enter : false,
16061     left : false,
16062     right : false,
16063     up : false,
16064     down : false,
16065     tab : false,
16066     esc : false,
16067     pageUp : false,
16068     pageDown : false,
16069     del : false,
16070     home : false,
16071     end : false,
16072
16073     // quick lookup hash
16074     keyToHandler : {
16075         37 : "left",
16076         39 : "right",
16077         38 : "up",
16078         40 : "down",
16079         33 : "pageUp",
16080         34 : "pageDown",
16081         46 : "del",
16082         36 : "home",
16083         35 : "end",
16084         13 : "enter",
16085         27 : "esc",
16086         9  : "tab"
16087     },
16088
16089         /**
16090          * Enable this KeyNav
16091          */
16092         enable: function(){
16093                 if(this.disabled){
16094             // ie won't do special keys on keypress, no one else will repeat keys with keydown
16095             // the EventObject will normalize Safari automatically
16096             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16097                 this.el.on("keydown", this.relay,  this);
16098             }else{
16099                 this.el.on("keydown", this.prepareEvent,  this);
16100                 this.el.on("keypress", this.relay,  this);
16101             }
16102                     this.disabled = false;
16103                 }
16104         },
16105
16106         /**
16107          * Disable this KeyNav
16108          */
16109         disable: function(){
16110                 if(!this.disabled){
16111                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16112                 this.el.un("keydown", this.relay);
16113             }else{
16114                 this.el.un("keydown", this.prepareEvent);
16115                 this.el.un("keypress", this.relay);
16116             }
16117                     this.disabled = true;
16118                 }
16119         }
16120 };/*
16121  * Based on:
16122  * Ext JS Library 1.1.1
16123  * Copyright(c) 2006-2007, Ext JS, LLC.
16124  *
16125  * Originally Released Under LGPL - original licence link has changed is not relivant.
16126  *
16127  * Fork - LGPL
16128  * <script type="text/javascript">
16129  */
16130
16131  
16132 /**
16133  * @class Roo.KeyMap
16134  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16135  * The constructor accepts the same config object as defined by {@link #addBinding}.
16136  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16137  * combination it will call the function with this signature (if the match is a multi-key
16138  * combination the callback will still be called only once): (String key, Roo.EventObject e)
16139  * A KeyMap can also handle a string representation of keys.<br />
16140  * Usage:
16141  <pre><code>
16142 // map one key by key code
16143 var map = new Roo.KeyMap("my-element", {
16144     key: 13, // or Roo.EventObject.ENTER
16145     fn: myHandler,
16146     scope: myObject
16147 });
16148
16149 // map multiple keys to one action by string
16150 var map = new Roo.KeyMap("my-element", {
16151     key: "a\r\n\t",
16152     fn: myHandler,
16153     scope: myObject
16154 });
16155
16156 // map multiple keys to multiple actions by strings and array of codes
16157 var map = new Roo.KeyMap("my-element", [
16158     {
16159         key: [10,13],
16160         fn: function(){ alert("Return was pressed"); }
16161     }, {
16162         key: "abc",
16163         fn: function(){ alert('a, b or c was pressed'); }
16164     }, {
16165         key: "\t",
16166         ctrl:true,
16167         shift:true,
16168         fn: function(){ alert('Control + shift + tab was pressed.'); }
16169     }
16170 ]);
16171 </code></pre>
16172  * <b>Note: A KeyMap starts enabled</b>
16173  * @constructor
16174  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16175  * @param {Object} config The config (see {@link #addBinding})
16176  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16177  */
16178 Roo.KeyMap = function(el, config, eventName){
16179     this.el  = Roo.get(el);
16180     this.eventName = eventName || "keydown";
16181     this.bindings = [];
16182     if(config){
16183         this.addBinding(config);
16184     }
16185     this.enable();
16186 };
16187
16188 Roo.KeyMap.prototype = {
16189     /**
16190      * True to stop the event from bubbling and prevent the default browser action if the
16191      * key was handled by the KeyMap (defaults to false)
16192      * @type Boolean
16193      */
16194     stopEvent : false,
16195
16196     /**
16197      * Add a new binding to this KeyMap. The following config object properties are supported:
16198      * <pre>
16199 Property    Type             Description
16200 ----------  ---------------  ----------------------------------------------------------------------
16201 key         String/Array     A single keycode or an array of keycodes to handle
16202 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16203 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16204 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16205 fn          Function         The function to call when KeyMap finds the expected key combination
16206 scope       Object           The scope of the callback function
16207 </pre>
16208      *
16209      * Usage:
16210      * <pre><code>
16211 // Create a KeyMap
16212 var map = new Roo.KeyMap(document, {
16213     key: Roo.EventObject.ENTER,
16214     fn: handleKey,
16215     scope: this
16216 });
16217
16218 //Add a new binding to the existing KeyMap later
16219 map.addBinding({
16220     key: 'abc',
16221     shift: true,
16222     fn: handleKey,
16223     scope: this
16224 });
16225 </code></pre>
16226      * @param {Object/Array} config A single KeyMap config or an array of configs
16227      */
16228         addBinding : function(config){
16229         if(config instanceof Array){
16230             for(var i = 0, len = config.length; i < len; i++){
16231                 this.addBinding(config[i]);
16232             }
16233             return;
16234         }
16235         var keyCode = config.key,
16236             shift = config.shift, 
16237             ctrl = config.ctrl, 
16238             alt = config.alt,
16239             fn = config.fn,
16240             scope = config.scope;
16241         if(typeof keyCode == "string"){
16242             var ks = [];
16243             var keyString = keyCode.toUpperCase();
16244             for(var j = 0, len = keyString.length; j < len; j++){
16245                 ks.push(keyString.charCodeAt(j));
16246             }
16247             keyCode = ks;
16248         }
16249         var keyArray = keyCode instanceof Array;
16250         var handler = function(e){
16251             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16252                 var k = e.getKey();
16253                 if(keyArray){
16254                     for(var i = 0, len = keyCode.length; i < len; i++){
16255                         if(keyCode[i] == k){
16256                           if(this.stopEvent){
16257                               e.stopEvent();
16258                           }
16259                           fn.call(scope || window, k, e);
16260                           return;
16261                         }
16262                     }
16263                 }else{
16264                     if(k == keyCode){
16265                         if(this.stopEvent){
16266                            e.stopEvent();
16267                         }
16268                         fn.call(scope || window, k, e);
16269                     }
16270                 }
16271             }
16272         };
16273         this.bindings.push(handler);  
16274         },
16275
16276     /**
16277      * Shorthand for adding a single key listener
16278      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16279      * following options:
16280      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16281      * @param {Function} fn The function to call
16282      * @param {Object} scope (optional) The scope of the function
16283      */
16284     on : function(key, fn, scope){
16285         var keyCode, shift, ctrl, alt;
16286         if(typeof key == "object" && !(key instanceof Array)){
16287             keyCode = key.key;
16288             shift = key.shift;
16289             ctrl = key.ctrl;
16290             alt = key.alt;
16291         }else{
16292             keyCode = key;
16293         }
16294         this.addBinding({
16295             key: keyCode,
16296             shift: shift,
16297             ctrl: ctrl,
16298             alt: alt,
16299             fn: fn,
16300             scope: scope
16301         })
16302     },
16303
16304     // private
16305     handleKeyDown : function(e){
16306             if(this.enabled){ //just in case
16307             var b = this.bindings;
16308             for(var i = 0, len = b.length; i < len; i++){
16309                 b[i].call(this, e);
16310             }
16311             }
16312         },
16313         
16314         /**
16315          * Returns true if this KeyMap is enabled
16316          * @return {Boolean} 
16317          */
16318         isEnabled : function(){
16319             return this.enabled;  
16320         },
16321         
16322         /**
16323          * Enables this KeyMap
16324          */
16325         enable: function(){
16326                 if(!this.enabled){
16327                     this.el.on(this.eventName, this.handleKeyDown, this);
16328                     this.enabled = true;
16329                 }
16330         },
16331
16332         /**
16333          * Disable this KeyMap
16334          */
16335         disable: function(){
16336                 if(this.enabled){
16337                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16338                     this.enabled = false;
16339                 }
16340         }
16341 };/*
16342  * Based on:
16343  * Ext JS Library 1.1.1
16344  * Copyright(c) 2006-2007, Ext JS, LLC.
16345  *
16346  * Originally Released Under LGPL - original licence link has changed is not relivant.
16347  *
16348  * Fork - LGPL
16349  * <script type="text/javascript">
16350  */
16351
16352  
16353 /**
16354  * @class Roo.util.TextMetrics
16355  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16356  * wide, in pixels, a given block of text will be.
16357  * @static
16358  */
16359 Roo.util.TextMetrics = function(){
16360     var shared;
16361     return {
16362         /**
16363          * Measures the size of the specified text
16364          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16365          * that can affect the size of the rendered text
16366          * @param {String} text The text to measure
16367          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16368          * in order to accurately measure the text height
16369          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16370          */
16371         measure : function(el, text, fixedWidth){
16372             if(!shared){
16373                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16374             }
16375             shared.bind(el);
16376             shared.setFixedWidth(fixedWidth || 'auto');
16377             return shared.getSize(text);
16378         },
16379
16380         /**
16381          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16382          * the overhead of multiple calls to initialize the style properties on each measurement.
16383          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16384          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16385          * in order to accurately measure the text height
16386          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16387          */
16388         createInstance : function(el, fixedWidth){
16389             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16390         }
16391     };
16392 }();
16393
16394 /**
16395  * @class Roo.util.TextMetrics.Instance
16396  * Instance of  TextMetrics Calcuation
16397  * @constructor
16398  * Create a new TextMetrics Instance
16399  * @param {Object} bindto
16400  * @param {Boolean} fixedWidth
16401  */
16402
16403 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16404 {
16405     var ml = new Roo.Element(document.createElement('div'));
16406     document.body.appendChild(ml.dom);
16407     ml.position('absolute');
16408     ml.setLeftTop(-1000, -1000);
16409     ml.hide();
16410
16411     if(fixedWidth){
16412         ml.setWidth(fixedWidth);
16413     }
16414      
16415     var instance = {
16416         /**
16417          * Returns the size of the specified text based on the internal element's style and width properties
16418          * @param {String} text The text to measure
16419          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16420          */
16421         getSize : function(text){
16422             ml.update(text);
16423             var s = ml.getSize();
16424             ml.update('');
16425             return s;
16426         },
16427
16428         /**
16429          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16430          * that can affect the size of the rendered text
16431          * @param {String/HTMLElement} el The element, dom node or id
16432          */
16433         bind : function(el){
16434             ml.setStyle(
16435                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16436             );
16437         },
16438
16439         /**
16440          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16441          * to set a fixed width in order to accurately measure the text height.
16442          * @param {Number} width The width to set on the element
16443          */
16444         setFixedWidth : function(width){
16445             ml.setWidth(width);
16446         },
16447
16448         /**
16449          * Returns the measured width of the specified text
16450          * @param {String} text The text to measure
16451          * @return {Number} width The width in pixels
16452          */
16453         getWidth : function(text){
16454             ml.dom.style.width = 'auto';
16455             return this.getSize(text).width;
16456         },
16457
16458         /**
16459          * Returns the measured height of the specified text.  For multiline text, be sure to call
16460          * {@link #setFixedWidth} if necessary.
16461          * @param {String} text The text to measure
16462          * @return {Number} height The height in pixels
16463          */
16464         getHeight : function(text){
16465             return this.getSize(text).height;
16466         }
16467     };
16468
16469     instance.bind(bindTo);
16470
16471     return instance;
16472 };
16473
16474 // backwards compat
16475 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16476  * Based on:
16477  * Ext JS Library 1.1.1
16478  * Copyright(c) 2006-2007, Ext JS, LLC.
16479  *
16480  * Originally Released Under LGPL - original licence link has changed is not relivant.
16481  *
16482  * Fork - LGPL
16483  * <script type="text/javascript">
16484  */
16485
16486 /**
16487  * @class Roo.state.Provider
16488  * Abstract base class for state provider implementations. This class provides methods
16489  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16490  * Provider interface.
16491  */
16492 Roo.state.Provider = function(){
16493     /**
16494      * @event statechange
16495      * Fires when a state change occurs.
16496      * @param {Provider} this This state provider
16497      * @param {String} key The state key which was changed
16498      * @param {String} value The encoded value for the state
16499      */
16500     this.addEvents({
16501         "statechange": true
16502     });
16503     this.state = {};
16504     Roo.state.Provider.superclass.constructor.call(this);
16505 };
16506 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16507     /**
16508      * Returns the current value for a key
16509      * @param {String} name The key name
16510      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16511      * @return {Mixed} The state data
16512      */
16513     get : function(name, defaultValue){
16514         return typeof this.state[name] == "undefined" ?
16515             defaultValue : this.state[name];
16516     },
16517     
16518     /**
16519      * Clears a value from the state
16520      * @param {String} name The key name
16521      */
16522     clear : function(name){
16523         delete this.state[name];
16524         this.fireEvent("statechange", this, name, null);
16525     },
16526     
16527     /**
16528      * Sets the value for a key
16529      * @param {String} name The key name
16530      * @param {Mixed} value The value to set
16531      */
16532     set : function(name, value){
16533         this.state[name] = value;
16534         this.fireEvent("statechange", this, name, value);
16535     },
16536     
16537     /**
16538      * Decodes a string previously encoded with {@link #encodeValue}.
16539      * @param {String} value The value to decode
16540      * @return {Mixed} The decoded value
16541      */
16542     decodeValue : function(cookie){
16543         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16544         var matches = re.exec(unescape(cookie));
16545         if(!matches || !matches[1]) {
16546             return; // non state cookie
16547         }
16548         var type = matches[1];
16549         var v = matches[2];
16550         switch(type){
16551             case "n":
16552                 return parseFloat(v);
16553             case "d":
16554                 return new Date(Date.parse(v));
16555             case "b":
16556                 return (v == "1");
16557             case "a":
16558                 var all = [];
16559                 var values = v.split("^");
16560                 for(var i = 0, len = values.length; i < len; i++){
16561                     all.push(this.decodeValue(values[i]));
16562                 }
16563                 return all;
16564            case "o":
16565                 var all = {};
16566                 var values = v.split("^");
16567                 for(var i = 0, len = values.length; i < len; i++){
16568                     var kv = values[i].split("=");
16569                     all[kv[0]] = this.decodeValue(kv[1]);
16570                 }
16571                 return all;
16572            default:
16573                 return v;
16574         }
16575     },
16576     
16577     /**
16578      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16579      * @param {Mixed} value The value to encode
16580      * @return {String} The encoded value
16581      */
16582     encodeValue : function(v){
16583         var enc;
16584         if(typeof v == "number"){
16585             enc = "n:" + v;
16586         }else if(typeof v == "boolean"){
16587             enc = "b:" + (v ? "1" : "0");
16588         }else if(v instanceof Date){
16589             enc = "d:" + v.toGMTString();
16590         }else if(v instanceof Array){
16591             var flat = "";
16592             for(var i = 0, len = v.length; i < len; i++){
16593                 flat += this.encodeValue(v[i]);
16594                 if(i != len-1) {
16595                     flat += "^";
16596                 }
16597             }
16598             enc = "a:" + flat;
16599         }else if(typeof v == "object"){
16600             var flat = "";
16601             for(var key in v){
16602                 if(typeof v[key] != "function"){
16603                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16604                 }
16605             }
16606             enc = "o:" + flat.substring(0, flat.length-1);
16607         }else{
16608             enc = "s:" + v;
16609         }
16610         return escape(enc);        
16611     }
16612 });
16613
16614 /*
16615  * Based on:
16616  * Ext JS Library 1.1.1
16617  * Copyright(c) 2006-2007, Ext JS, LLC.
16618  *
16619  * Originally Released Under LGPL - original licence link has changed is not relivant.
16620  *
16621  * Fork - LGPL
16622  * <script type="text/javascript">
16623  */
16624 /**
16625  * @class Roo.state.Manager
16626  * This is the global state manager. By default all components that are "state aware" check this class
16627  * for state information if you don't pass them a custom state provider. In order for this class
16628  * to be useful, it must be initialized with a provider when your application initializes.
16629  <pre><code>
16630 // in your initialization function
16631 init : function(){
16632    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16633    ...
16634    // supposed you have a {@link Roo.BorderLayout}
16635    var layout = new Roo.BorderLayout(...);
16636    layout.restoreState();
16637    // or a {Roo.BasicDialog}
16638    var dialog = new Roo.BasicDialog(...);
16639    dialog.restoreState();
16640  </code></pre>
16641  * @static
16642  */
16643 Roo.state.Manager = function(){
16644     var provider = new Roo.state.Provider();
16645     
16646     return {
16647         /**
16648          * Configures the default state provider for your application
16649          * @param {Provider} stateProvider The state provider to set
16650          */
16651         setProvider : function(stateProvider){
16652             provider = stateProvider;
16653         },
16654         
16655         /**
16656          * Returns the current value for a key
16657          * @param {String} name The key name
16658          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16659          * @return {Mixed} The state data
16660          */
16661         get : function(key, defaultValue){
16662             return provider.get(key, defaultValue);
16663         },
16664         
16665         /**
16666          * Sets the value for a key
16667          * @param {String} name The key name
16668          * @param {Mixed} value The state data
16669          */
16670          set : function(key, value){
16671             provider.set(key, value);
16672         },
16673         
16674         /**
16675          * Clears a value from the state
16676          * @param {String} name The key name
16677          */
16678         clear : function(key){
16679             provider.clear(key);
16680         },
16681         
16682         /**
16683          * Gets the currently configured state provider
16684          * @return {Provider} The state provider
16685          */
16686         getProvider : function(){
16687             return provider;
16688         }
16689     };
16690 }();
16691 /*
16692  * Based on:
16693  * Ext JS Library 1.1.1
16694  * Copyright(c) 2006-2007, Ext JS, LLC.
16695  *
16696  * Originally Released Under LGPL - original licence link has changed is not relivant.
16697  *
16698  * Fork - LGPL
16699  * <script type="text/javascript">
16700  */
16701 /**
16702  * @class Roo.state.CookieProvider
16703  * @extends Roo.state.Provider
16704  * The default Provider implementation which saves state via cookies.
16705  * <br />Usage:
16706  <pre><code>
16707    var cp = new Roo.state.CookieProvider({
16708        path: "/cgi-bin/",
16709        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16710        domain: "roojs.com"
16711    })
16712    Roo.state.Manager.setProvider(cp);
16713  </code></pre>
16714  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16715  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16716  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16717  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16718  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16719  * domain the page is running on including the 'www' like 'www.roojs.com')
16720  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16721  * @constructor
16722  * Create a new CookieProvider
16723  * @param {Object} config The configuration object
16724  */
16725 Roo.state.CookieProvider = function(config){
16726     Roo.state.CookieProvider.superclass.constructor.call(this);
16727     this.path = "/";
16728     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16729     this.domain = null;
16730     this.secure = false;
16731     Roo.apply(this, config);
16732     this.state = this.readCookies();
16733 };
16734
16735 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16736     // private
16737     set : function(name, value){
16738         if(typeof value == "undefined" || value === null){
16739             this.clear(name);
16740             return;
16741         }
16742         this.setCookie(name, value);
16743         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16744     },
16745
16746     // private
16747     clear : function(name){
16748         this.clearCookie(name);
16749         Roo.state.CookieProvider.superclass.clear.call(this, name);
16750     },
16751
16752     // private
16753     readCookies : function(){
16754         var cookies = {};
16755         var c = document.cookie + ";";
16756         var re = /\s?(.*?)=(.*?);/g;
16757         var matches;
16758         while((matches = re.exec(c)) != null){
16759             var name = matches[1];
16760             var value = matches[2];
16761             if(name && name.substring(0,3) == "ys-"){
16762                 cookies[name.substr(3)] = this.decodeValue(value);
16763             }
16764         }
16765         return cookies;
16766     },
16767
16768     // private
16769     setCookie : function(name, value){
16770         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16771            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16772            ((this.path == null) ? "" : ("; path=" + this.path)) +
16773            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16774            ((this.secure == true) ? "; secure" : "");
16775     },
16776
16777     // private
16778     clearCookie : function(name){
16779         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16780            ((this.path == null) ? "" : ("; path=" + this.path)) +
16781            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16782            ((this.secure == true) ? "; secure" : "");
16783     }
16784 });/*
16785  * Based on:
16786  * Ext JS Library 1.1.1
16787  * Copyright(c) 2006-2007, Ext JS, LLC.
16788  *
16789  * Originally Released Under LGPL - original licence link has changed is not relivant.
16790  *
16791  * Fork - LGPL
16792  * <script type="text/javascript">
16793  */
16794  
16795
16796 /**
16797  * @class Roo.ComponentMgr
16798  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16799  * @static
16800  */
16801 Roo.ComponentMgr = function(){
16802     var all = new Roo.util.MixedCollection();
16803
16804     return {
16805         /**
16806          * Registers a component.
16807          * @param {Roo.Component} c The component
16808          */
16809         register : function(c){
16810             all.add(c);
16811         },
16812
16813         /**
16814          * Unregisters a component.
16815          * @param {Roo.Component} c The component
16816          */
16817         unregister : function(c){
16818             all.remove(c);
16819         },
16820
16821         /**
16822          * Returns a component by id
16823          * @param {String} id The component id
16824          */
16825         get : function(id){
16826             return all.get(id);
16827         },
16828
16829         /**
16830          * Registers a function that will be called when a specified component is added to ComponentMgr
16831          * @param {String} id The component id
16832          * @param {Funtction} fn The callback function
16833          * @param {Object} scope The scope of the callback
16834          */
16835         onAvailable : function(id, fn, scope){
16836             all.on("add", function(index, o){
16837                 if(o.id == id){
16838                     fn.call(scope || o, o);
16839                     all.un("add", fn, scope);
16840                 }
16841             });
16842         }
16843     };
16844 }();/*
16845  * Based on:
16846  * Ext JS Library 1.1.1
16847  * Copyright(c) 2006-2007, Ext JS, LLC.
16848  *
16849  * Originally Released Under LGPL - original licence link has changed is not relivant.
16850  *
16851  * Fork - LGPL
16852  * <script type="text/javascript">
16853  */
16854  
16855 /**
16856  * @class Roo.Component
16857  * @extends Roo.util.Observable
16858  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16859  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16860  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16861  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16862  * All visual components (widgets) that require rendering into a layout should subclass Component.
16863  * @constructor
16864  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16865  * 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
16866  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16867  */
16868 Roo.Component = function(config){
16869     console.log("COMPONENT CONSTRUCTOR");
16870     config = config || {};
16871     if(config.tagName || config.dom || typeof config == "string"){ // element object
16872         config = {el: config, id: config.id || config};
16873     }
16874     this.initialConfig = config;
16875
16876     Roo.apply(this, config);
16877     this.addEvents({
16878         /**
16879          * @event disable
16880          * Fires after the component is disabled.
16881              * @param {Roo.Component} this
16882              */
16883         disable : true,
16884         /**
16885          * @event enable
16886          * Fires after the component is enabled.
16887              * @param {Roo.Component} this
16888              */
16889         enable : true,
16890         /**
16891          * @event beforeshow
16892          * Fires before the component is shown.  Return false to stop the show.
16893              * @param {Roo.Component} this
16894              */
16895         beforeshow : true,
16896         /**
16897          * @event show
16898          * Fires after the component is shown.
16899              * @param {Roo.Component} this
16900              */
16901         show : true,
16902         /**
16903          * @event beforehide
16904          * Fires before the component is hidden. Return false to stop the hide.
16905              * @param {Roo.Component} this
16906              */
16907         beforehide : true,
16908         /**
16909          * @event hide
16910          * Fires after the component is hidden.
16911              * @param {Roo.Component} this
16912              */
16913         hide : true,
16914         /**
16915          * @event beforerender
16916          * Fires before the component is rendered. Return false to stop the render.
16917              * @param {Roo.Component} this
16918              */
16919         beforerender : true,
16920         /**
16921          * @event render
16922          * Fires after the component is rendered.
16923              * @param {Roo.Component} this
16924              */
16925         render : true,
16926         /**
16927          * @event beforedestroy
16928          * Fires before the component is destroyed. Return false to stop the destroy.
16929              * @param {Roo.Component} this
16930              */
16931         beforedestroy : true,
16932         /**
16933          * @event destroy
16934          * Fires after the component is destroyed.
16935              * @param {Roo.Component} this
16936              */
16937         destroy : true
16938     });
16939     if(!this.id){
16940         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16941     }
16942     Roo.ComponentMgr.register(this);
16943     Roo.Component.superclass.constructor.call(this);
16944     this.initComponent();
16945     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16946         this.render(this.renderTo);
16947         delete this.renderTo;
16948     }
16949 };
16950
16951 /** @private */
16952 Roo.Component.AUTO_ID = 1000;
16953
16954 Roo.extend(Roo.Component, Roo.util.Observable, {
16955     /**
16956      * @scope Roo.Component.prototype
16957      * @type {Boolean}
16958      * true if this component is hidden. Read-only.
16959      */
16960     hidden : false,
16961     /**
16962      * @type {Boolean}
16963      * true if this component is disabled. Read-only.
16964      */
16965     disabled : false,
16966     /**
16967      * @type {Boolean}
16968      * true if this component has been rendered. Read-only.
16969      */
16970     rendered : false,
16971     
16972     /** @cfg {String} disableClass
16973      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16974      */
16975     disabledClass : "x-item-disabled",
16976         /** @cfg {Boolean} allowDomMove
16977          * Whether the component can move the Dom node when rendering (defaults to true).
16978          */
16979     allowDomMove : true,
16980     /** @cfg {String} hideMode (display|visibility)
16981      * How this component should hidden. Supported values are
16982      * "visibility" (css visibility), "offsets" (negative offset position) and
16983      * "display" (css display) - defaults to "display".
16984      */
16985     hideMode: 'display',
16986
16987     /** @private */
16988     ctype : "Roo.Component",
16989
16990     /**
16991      * @cfg {String} actionMode 
16992      * which property holds the element that used for  hide() / show() / disable() / enable()
16993      * default is 'el' for forms you probably want to set this to fieldEl 
16994      */
16995     actionMode : "el",
16996
16997     /** @private */
16998     getActionEl : function(){
16999         return this[this.actionMode];
17000     },
17001
17002     initComponent : Roo.emptyFn,
17003     /**
17004      * If this is a lazy rendering component, render it to its container element.
17005      * @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.
17006      */
17007     render : function(container, position){
17008         
17009         if(this.rendered){
17010             return this;
17011         }
17012         
17013         if(this.fireEvent("beforerender", this) === false){
17014             return false;
17015         }
17016         
17017         if(!container && this.el){
17018             this.el = Roo.get(this.el);
17019             container = this.el.dom.parentNode;
17020             this.allowDomMove = false;
17021         }
17022         this.container = Roo.get(container);
17023         this.rendered = true;
17024         if(position !== undefined){
17025             if(typeof position == 'number'){
17026                 position = this.container.dom.childNodes[position];
17027             }else{
17028                 position = Roo.getDom(position);
17029             }
17030         }
17031         this.onRender(this.container, position || null);
17032         if(this.cls){
17033             this.el.addClass(this.cls);
17034             delete this.cls;
17035         }
17036         if(this.style){
17037             this.el.applyStyles(this.style);
17038             delete this.style;
17039         }
17040         this.fireEvent("render", this);
17041         this.afterRender(this.container);
17042         if(this.hidden){
17043             this.hide();
17044         }
17045         if(this.disabled){
17046             this.disable();
17047         }
17048
17049         return this;
17050         
17051     },
17052
17053     /** @private */
17054     // default function is not really useful
17055     onRender : function(ct, position){
17056         console.log("THIS EL");
17057         console.log(this.el);
17058         if(this.el){
17059             this.el = Roo.get(this.el);
17060             if(this.allowDomMove !== false){
17061                 ct.dom.insertBefore(this.el.dom, position);
17062             }
17063         }
17064     },
17065
17066     /** @private */
17067     getAutoCreate : function(){
17068         var cfg = typeof this.autoCreate == "object" ?
17069                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17070         if(this.id && !cfg.id){
17071             cfg.id = this.id;
17072         }
17073         return cfg;
17074     },
17075
17076     /** @private */
17077     afterRender : Roo.emptyFn,
17078
17079     /**
17080      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17081      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17082      */
17083     destroy : function(){
17084         if(this.fireEvent("beforedestroy", this) !== false){
17085             this.purgeListeners();
17086             this.beforeDestroy();
17087             if(this.rendered){
17088                 this.el.removeAllListeners();
17089                 this.el.remove();
17090                 if(this.actionMode == "container"){
17091                     this.container.remove();
17092                 }
17093             }
17094             this.onDestroy();
17095             Roo.ComponentMgr.unregister(this);
17096             this.fireEvent("destroy", this);
17097         }
17098     },
17099
17100         /** @private */
17101     beforeDestroy : function(){
17102
17103     },
17104
17105         /** @private */
17106         onDestroy : function(){
17107
17108     },
17109
17110     /**
17111      * Returns the underlying {@link Roo.Element}.
17112      * @return {Roo.Element} The element
17113      */
17114     getEl : function(){
17115         return this.el;
17116     },
17117
17118     /**
17119      * Returns the id of this component.
17120      * @return {String}
17121      */
17122     getId : function(){
17123         return this.id;
17124     },
17125
17126     /**
17127      * Try to focus this component.
17128      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17129      * @return {Roo.Component} this
17130      */
17131     focus : function(selectText){
17132         if(this.rendered){
17133             this.el.focus();
17134             if(selectText === true){
17135                 this.el.dom.select();
17136             }
17137         }
17138         return this;
17139     },
17140
17141     /** @private */
17142     blur : function(){
17143         if(this.rendered){
17144             this.el.blur();
17145         }
17146         return this;
17147     },
17148
17149     /**
17150      * Disable this component.
17151      * @return {Roo.Component} this
17152      */
17153     disable : function(){
17154         if(this.rendered){
17155             this.onDisable();
17156         }
17157         this.disabled = true;
17158         this.fireEvent("disable", this);
17159         return this;
17160     },
17161
17162         // private
17163     onDisable : function(){
17164         this.getActionEl().addClass(this.disabledClass);
17165         this.el.dom.disabled = true;
17166     },
17167
17168     /**
17169      * Enable this component.
17170      * @return {Roo.Component} this
17171      */
17172     enable : function(){
17173         if(this.rendered){
17174             this.onEnable();
17175         }
17176         this.disabled = false;
17177         this.fireEvent("enable", this);
17178         return this;
17179     },
17180
17181         // private
17182     onEnable : function(){
17183         this.getActionEl().removeClass(this.disabledClass);
17184         this.el.dom.disabled = false;
17185     },
17186
17187     /**
17188      * Convenience function for setting disabled/enabled by boolean.
17189      * @param {Boolean} disabled
17190      */
17191     setDisabled : function(disabled){
17192         this[disabled ? "disable" : "enable"]();
17193     },
17194
17195     /**
17196      * Show this component.
17197      * @return {Roo.Component} this
17198      */
17199     show: function(){
17200         if(this.fireEvent("beforeshow", this) !== false){
17201             this.hidden = false;
17202             if(this.rendered){
17203                 this.onShow();
17204             }
17205             this.fireEvent("show", this);
17206         }
17207         return this;
17208     },
17209
17210     // private
17211     onShow : function(){
17212         var ae = this.getActionEl();
17213         if(this.hideMode == 'visibility'){
17214             ae.dom.style.visibility = "visible";
17215         }else if(this.hideMode == 'offsets'){
17216             ae.removeClass('x-hidden');
17217         }else{
17218             ae.dom.style.display = "";
17219         }
17220     },
17221
17222     /**
17223      * Hide this component.
17224      * @return {Roo.Component} this
17225      */
17226     hide: function(){
17227         if(this.fireEvent("beforehide", this) !== false){
17228             this.hidden = true;
17229             if(this.rendered){
17230                 this.onHide();
17231             }
17232             this.fireEvent("hide", this);
17233         }
17234         return this;
17235     },
17236
17237     // private
17238     onHide : function(){
17239         var ae = this.getActionEl();
17240         if(this.hideMode == 'visibility'){
17241             ae.dom.style.visibility = "hidden";
17242         }else if(this.hideMode == 'offsets'){
17243             ae.addClass('x-hidden');
17244         }else{
17245             ae.dom.style.display = "none";
17246         }
17247     },
17248
17249     /**
17250      * Convenience function to hide or show this component by boolean.
17251      * @param {Boolean} visible True to show, false to hide
17252      * @return {Roo.Component} this
17253      */
17254     setVisible: function(visible){
17255         if(visible) {
17256             this.show();
17257         }else{
17258             this.hide();
17259         }
17260         return this;
17261     },
17262
17263     /**
17264      * Returns true if this component is visible.
17265      */
17266     isVisible : function(){
17267         return this.getActionEl().isVisible();
17268     },
17269
17270     cloneConfig : function(overrides){
17271         overrides = overrides || {};
17272         var id = overrides.id || Roo.id();
17273         var cfg = Roo.applyIf(overrides, this.initialConfig);
17274         cfg.id = id; // prevent dup id
17275         return new this.constructor(cfg);
17276     }
17277 });/*
17278  * Based on:
17279  * Ext JS Library 1.1.1
17280  * Copyright(c) 2006-2007, Ext JS, LLC.
17281  *
17282  * Originally Released Under LGPL - original licence link has changed is not relivant.
17283  *
17284  * Fork - LGPL
17285  * <script type="text/javascript">
17286  */
17287
17288 /**
17289  * @class Roo.BoxComponent
17290  * @extends Roo.Component
17291  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17292  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17293  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17294  * layout containers.
17295  * @constructor
17296  * @param {Roo.Element/String/Object} config The configuration options.
17297  */
17298 Roo.BoxComponent = function(config){
17299     Roo.Component.call(this, config);
17300     this.addEvents({
17301         /**
17302          * @event resize
17303          * Fires after the component is resized.
17304              * @param {Roo.Component} this
17305              * @param {Number} adjWidth The box-adjusted width that was set
17306              * @param {Number} adjHeight The box-adjusted height that was set
17307              * @param {Number} rawWidth The width that was originally specified
17308              * @param {Number} rawHeight The height that was originally specified
17309              */
17310         resize : true,
17311         /**
17312          * @event move
17313          * Fires after the component is moved.
17314              * @param {Roo.Component} this
17315              * @param {Number} x The new x position
17316              * @param {Number} y The new y position
17317              */
17318         move : true
17319     });
17320 };
17321
17322 Roo.extend(Roo.BoxComponent, Roo.Component, {
17323     // private, set in afterRender to signify that the component has been rendered
17324     boxReady : false,
17325     // private, used to defer height settings to subclasses
17326     deferHeight: false,
17327     /** @cfg {Number} width
17328      * width (optional) size of component
17329      */
17330      /** @cfg {Number} height
17331      * height (optional) size of component
17332      */
17333      
17334     /**
17335      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17336      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17337      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17338      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17339      * @return {Roo.BoxComponent} this
17340      */
17341     setSize : function(w, h){
17342         // support for standard size objects
17343         if(typeof w == 'object'){
17344             h = w.height;
17345             w = w.width;
17346         }
17347         // not rendered
17348         if(!this.boxReady){
17349             this.width = w;
17350             this.height = h;
17351             return this;
17352         }
17353
17354         // prevent recalcs when not needed
17355         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17356             return this;
17357         }
17358         this.lastSize = {width: w, height: h};
17359
17360         var adj = this.adjustSize(w, h);
17361         var aw = adj.width, ah = adj.height;
17362         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17363             var rz = this.getResizeEl();
17364             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17365                 rz.setSize(aw, ah);
17366             }else if(!this.deferHeight && ah !== undefined){
17367                 rz.setHeight(ah);
17368             }else if(aw !== undefined){
17369                 rz.setWidth(aw);
17370             }
17371             this.onResize(aw, ah, w, h);
17372             this.fireEvent('resize', this, aw, ah, w, h);
17373         }
17374         return this;
17375     },
17376
17377     /**
17378      * Gets the current size of the component's underlying element.
17379      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17380      */
17381     getSize : function(){
17382         return this.el.getSize();
17383     },
17384
17385     /**
17386      * Gets the current XY position of the component's underlying element.
17387      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17388      * @return {Array} The XY position of the element (e.g., [100, 200])
17389      */
17390     getPosition : function(local){
17391         if(local === true){
17392             return [this.el.getLeft(true), this.el.getTop(true)];
17393         }
17394         return this.xy || this.el.getXY();
17395     },
17396
17397     /**
17398      * Gets the current box measurements of the component's underlying element.
17399      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17400      * @returns {Object} box An object in the format {x, y, width, height}
17401      */
17402     getBox : function(local){
17403         var s = this.el.getSize();
17404         if(local){
17405             s.x = this.el.getLeft(true);
17406             s.y = this.el.getTop(true);
17407         }else{
17408             var xy = this.xy || this.el.getXY();
17409             s.x = xy[0];
17410             s.y = xy[1];
17411         }
17412         return s;
17413     },
17414
17415     /**
17416      * Sets the current box measurements of the component's underlying element.
17417      * @param {Object} box An object in the format {x, y, width, height}
17418      * @returns {Roo.BoxComponent} this
17419      */
17420     updateBox : function(box){
17421         this.setSize(box.width, box.height);
17422         this.setPagePosition(box.x, box.y);
17423         return this;
17424     },
17425
17426     // protected
17427     getResizeEl : function(){
17428         return this.resizeEl || this.el;
17429     },
17430
17431     // protected
17432     getPositionEl : function(){
17433         return this.positionEl || this.el;
17434     },
17435
17436     /**
17437      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17438      * This method fires the move event.
17439      * @param {Number} left The new left
17440      * @param {Number} top The new top
17441      * @returns {Roo.BoxComponent} this
17442      */
17443     setPosition : function(x, y){
17444         this.x = x;
17445         this.y = y;
17446         if(!this.boxReady){
17447             return this;
17448         }
17449         var adj = this.adjustPosition(x, y);
17450         var ax = adj.x, ay = adj.y;
17451
17452         var el = this.getPositionEl();
17453         if(ax !== undefined || ay !== undefined){
17454             if(ax !== undefined && ay !== undefined){
17455                 el.setLeftTop(ax, ay);
17456             }else if(ax !== undefined){
17457                 el.setLeft(ax);
17458             }else if(ay !== undefined){
17459                 el.setTop(ay);
17460             }
17461             this.onPosition(ax, ay);
17462             this.fireEvent('move', this, ax, ay);
17463         }
17464         return this;
17465     },
17466
17467     /**
17468      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17469      * This method fires the move event.
17470      * @param {Number} x The new x position
17471      * @param {Number} y The new y position
17472      * @returns {Roo.BoxComponent} this
17473      */
17474     setPagePosition : function(x, y){
17475         this.pageX = x;
17476         this.pageY = y;
17477         if(!this.boxReady){
17478             return;
17479         }
17480         if(x === undefined || y === undefined){ // cannot translate undefined points
17481             return;
17482         }
17483         var p = this.el.translatePoints(x, y);
17484         this.setPosition(p.left, p.top);
17485         return this;
17486     },
17487
17488     // private
17489     onRender : function(ct, position){
17490         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17491         if(this.resizeEl){
17492             this.resizeEl = Roo.get(this.resizeEl);
17493         }
17494         if(this.positionEl){
17495             this.positionEl = Roo.get(this.positionEl);
17496         }
17497     },
17498
17499     // private
17500     afterRender : function(){
17501         Roo.BoxComponent.superclass.afterRender.call(this);
17502         this.boxReady = true;
17503         this.setSize(this.width, this.height);
17504         if(this.x || this.y){
17505             this.setPosition(this.x, this.y);
17506         }
17507         if(this.pageX || this.pageY){
17508             this.setPagePosition(this.pageX, this.pageY);
17509         }
17510     },
17511
17512     /**
17513      * Force the component's size to recalculate based on the underlying element's current height and width.
17514      * @returns {Roo.BoxComponent} this
17515      */
17516     syncSize : function(){
17517         delete this.lastSize;
17518         this.setSize(this.el.getWidth(), this.el.getHeight());
17519         return this;
17520     },
17521
17522     /**
17523      * Called after the component is resized, this method is empty by default but can be implemented by any
17524      * subclass that needs to perform custom logic after a resize occurs.
17525      * @param {Number} adjWidth The box-adjusted width that was set
17526      * @param {Number} adjHeight The box-adjusted height that was set
17527      * @param {Number} rawWidth The width that was originally specified
17528      * @param {Number} rawHeight The height that was originally specified
17529      */
17530     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17531
17532     },
17533
17534     /**
17535      * Called after the component is moved, this method is empty by default but can be implemented by any
17536      * subclass that needs to perform custom logic after a move occurs.
17537      * @param {Number} x The new x position
17538      * @param {Number} y The new y position
17539      */
17540     onPosition : function(x, y){
17541
17542     },
17543
17544     // private
17545     adjustSize : function(w, h){
17546         if(this.autoWidth){
17547             w = 'auto';
17548         }
17549         if(this.autoHeight){
17550             h = 'auto';
17551         }
17552         return {width : w, height: h};
17553     },
17554
17555     // private
17556     adjustPosition : function(x, y){
17557         return {x : x, y: y};
17558     }
17559 });/*
17560  * Based on:
17561  * Ext JS Library 1.1.1
17562  * Copyright(c) 2006-2007, Ext JS, LLC.
17563  *
17564  * Originally Released Under LGPL - original licence link has changed is not relivant.
17565  *
17566  * Fork - LGPL
17567  * <script type="text/javascript">
17568  */
17569  (function(){ 
17570 /**
17571  * @class Roo.Layer
17572  * @extends Roo.Element
17573  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17574  * automatic maintaining of shadow/shim positions.
17575  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17576  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17577  * you can pass a string with a CSS class name. False turns off the shadow.
17578  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17579  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17580  * @cfg {String} cls CSS class to add to the element
17581  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17582  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17583  * @constructor
17584  * @param {Object} config An object with config options.
17585  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17586  */
17587
17588 Roo.Layer = function(config, existingEl){
17589     config = config || {};
17590     var dh = Roo.DomHelper;
17591     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17592     if(existingEl){
17593         this.dom = Roo.getDom(existingEl);
17594     }
17595     if(!this.dom){
17596         var o = config.dh || {tag: "div", cls: "x-layer"};
17597         this.dom = dh.append(pel, o);
17598     }
17599     if(config.cls){
17600         this.addClass(config.cls);
17601     }
17602     this.constrain = config.constrain !== false;
17603     this.visibilityMode = Roo.Element.VISIBILITY;
17604     if(config.id){
17605         this.id = this.dom.id = config.id;
17606     }else{
17607         this.id = Roo.id(this.dom);
17608     }
17609     this.zindex = config.zindex || this.getZIndex();
17610     this.position("absolute", this.zindex);
17611     if(config.shadow){
17612         this.shadowOffset = config.shadowOffset || 4;
17613         this.shadow = new Roo.Shadow({
17614             offset : this.shadowOffset,
17615             mode : config.shadow
17616         });
17617     }else{
17618         this.shadowOffset = 0;
17619     }
17620     this.useShim = config.shim !== false && Roo.useShims;
17621     this.useDisplay = config.useDisplay;
17622     this.hide();
17623 };
17624
17625 var supr = Roo.Element.prototype;
17626
17627 // shims are shared among layer to keep from having 100 iframes
17628 var shims = [];
17629
17630 Roo.extend(Roo.Layer, Roo.Element, {
17631
17632     getZIndex : function(){
17633         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17634     },
17635
17636     getShim : function(){
17637         if(!this.useShim){
17638             return null;
17639         }
17640         if(this.shim){
17641             return this.shim;
17642         }
17643         var shim = shims.shift();
17644         if(!shim){
17645             shim = this.createShim();
17646             shim.enableDisplayMode('block');
17647             shim.dom.style.display = 'none';
17648             shim.dom.style.visibility = 'visible';
17649         }
17650         var pn = this.dom.parentNode;
17651         if(shim.dom.parentNode != pn){
17652             pn.insertBefore(shim.dom, this.dom);
17653         }
17654         shim.setStyle('z-index', this.getZIndex()-2);
17655         this.shim = shim;
17656         return shim;
17657     },
17658
17659     hideShim : function(){
17660         if(this.shim){
17661             this.shim.setDisplayed(false);
17662             shims.push(this.shim);
17663             delete this.shim;
17664         }
17665     },
17666
17667     disableShadow : function(){
17668         if(this.shadow){
17669             this.shadowDisabled = true;
17670             this.shadow.hide();
17671             this.lastShadowOffset = this.shadowOffset;
17672             this.shadowOffset = 0;
17673         }
17674     },
17675
17676     enableShadow : function(show){
17677         if(this.shadow){
17678             this.shadowDisabled = false;
17679             this.shadowOffset = this.lastShadowOffset;
17680             delete this.lastShadowOffset;
17681             if(show){
17682                 this.sync(true);
17683             }
17684         }
17685     },
17686
17687     // private
17688     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17689     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17690     sync : function(doShow){
17691         var sw = this.shadow;
17692         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17693             var sh = this.getShim();
17694
17695             var w = this.getWidth(),
17696                 h = this.getHeight();
17697
17698             var l = this.getLeft(true),
17699                 t = this.getTop(true);
17700
17701             if(sw && !this.shadowDisabled){
17702                 if(doShow && !sw.isVisible()){
17703                     sw.show(this);
17704                 }else{
17705                     sw.realign(l, t, w, h);
17706                 }
17707                 if(sh){
17708                     if(doShow){
17709                        sh.show();
17710                     }
17711                     // fit the shim behind the shadow, so it is shimmed too
17712                     var a = sw.adjusts, s = sh.dom.style;
17713                     s.left = (Math.min(l, l+a.l))+"px";
17714                     s.top = (Math.min(t, t+a.t))+"px";
17715                     s.width = (w+a.w)+"px";
17716                     s.height = (h+a.h)+"px";
17717                 }
17718             }else if(sh){
17719                 if(doShow){
17720                    sh.show();
17721                 }
17722                 sh.setSize(w, h);
17723                 sh.setLeftTop(l, t);
17724             }
17725             
17726         }
17727     },
17728
17729     // private
17730     destroy : function(){
17731         this.hideShim();
17732         if(this.shadow){
17733             this.shadow.hide();
17734         }
17735         this.removeAllListeners();
17736         var pn = this.dom.parentNode;
17737         if(pn){
17738             pn.removeChild(this.dom);
17739         }
17740         Roo.Element.uncache(this.id);
17741     },
17742
17743     remove : function(){
17744         this.destroy();
17745     },
17746
17747     // private
17748     beginUpdate : function(){
17749         this.updating = true;
17750     },
17751
17752     // private
17753     endUpdate : function(){
17754         this.updating = false;
17755         this.sync(true);
17756     },
17757
17758     // private
17759     hideUnders : function(negOffset){
17760         if(this.shadow){
17761             this.shadow.hide();
17762         }
17763         this.hideShim();
17764     },
17765
17766     // private
17767     constrainXY : function(){
17768         if(this.constrain){
17769             var vw = Roo.lib.Dom.getViewWidth(),
17770                 vh = Roo.lib.Dom.getViewHeight();
17771             var s = Roo.get(document).getScroll();
17772
17773             var xy = this.getXY();
17774             var x = xy[0], y = xy[1];   
17775             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17776             // only move it if it needs it
17777             var moved = false;
17778             // first validate right/bottom
17779             if((x + w) > vw+s.left){
17780                 x = vw - w - this.shadowOffset;
17781                 moved = true;
17782             }
17783             if((y + h) > vh+s.top){
17784                 y = vh - h - this.shadowOffset;
17785                 moved = true;
17786             }
17787             // then make sure top/left isn't negative
17788             if(x < s.left){
17789                 x = s.left;
17790                 moved = true;
17791             }
17792             if(y < s.top){
17793                 y = s.top;
17794                 moved = true;
17795             }
17796             if(moved){
17797                 if(this.avoidY){
17798                     var ay = this.avoidY;
17799                     if(y <= ay && (y+h) >= ay){
17800                         y = ay-h-5;   
17801                     }
17802                 }
17803                 xy = [x, y];
17804                 this.storeXY(xy);
17805                 supr.setXY.call(this, xy);
17806                 this.sync();
17807             }
17808         }
17809     },
17810
17811     isVisible : function(){
17812         return this.visible;    
17813     },
17814
17815     // private
17816     showAction : function(){
17817         this.visible = true; // track visibility to prevent getStyle calls
17818         if(this.useDisplay === true){
17819             this.setDisplayed("");
17820         }else if(this.lastXY){
17821             supr.setXY.call(this, this.lastXY);
17822         }else if(this.lastLT){
17823             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17824         }
17825     },
17826
17827     // private
17828     hideAction : function(){
17829         this.visible = false;
17830         if(this.useDisplay === true){
17831             this.setDisplayed(false);
17832         }else{
17833             this.setLeftTop(-10000,-10000);
17834         }
17835     },
17836
17837     // overridden Element method
17838     setVisible : function(v, a, d, c, e){
17839         if(v){
17840             this.showAction();
17841         }
17842         if(a && v){
17843             var cb = function(){
17844                 this.sync(true);
17845                 if(c){
17846                     c();
17847                 }
17848             }.createDelegate(this);
17849             supr.setVisible.call(this, true, true, d, cb, e);
17850         }else{
17851             if(!v){
17852                 this.hideUnders(true);
17853             }
17854             var cb = c;
17855             if(a){
17856                 cb = function(){
17857                     this.hideAction();
17858                     if(c){
17859                         c();
17860                     }
17861                 }.createDelegate(this);
17862             }
17863             supr.setVisible.call(this, v, a, d, cb, e);
17864             if(v){
17865                 this.sync(true);
17866             }else if(!a){
17867                 this.hideAction();
17868             }
17869         }
17870     },
17871
17872     storeXY : function(xy){
17873         delete this.lastLT;
17874         this.lastXY = xy;
17875     },
17876
17877     storeLeftTop : function(left, top){
17878         delete this.lastXY;
17879         this.lastLT = [left, top];
17880     },
17881
17882     // private
17883     beforeFx : function(){
17884         this.beforeAction();
17885         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17886     },
17887
17888     // private
17889     afterFx : function(){
17890         Roo.Layer.superclass.afterFx.apply(this, arguments);
17891         this.sync(this.isVisible());
17892     },
17893
17894     // private
17895     beforeAction : function(){
17896         if(!this.updating && this.shadow){
17897             this.shadow.hide();
17898         }
17899     },
17900
17901     // overridden Element method
17902     setLeft : function(left){
17903         this.storeLeftTop(left, this.getTop(true));
17904         supr.setLeft.apply(this, arguments);
17905         this.sync();
17906     },
17907
17908     setTop : function(top){
17909         this.storeLeftTop(this.getLeft(true), top);
17910         supr.setTop.apply(this, arguments);
17911         this.sync();
17912     },
17913
17914     setLeftTop : function(left, top){
17915         this.storeLeftTop(left, top);
17916         supr.setLeftTop.apply(this, arguments);
17917         this.sync();
17918     },
17919
17920     setXY : function(xy, a, d, c, e){
17921         this.fixDisplay();
17922         this.beforeAction();
17923         this.storeXY(xy);
17924         var cb = this.createCB(c);
17925         supr.setXY.call(this, xy, a, d, cb, e);
17926         if(!a){
17927             cb();
17928         }
17929     },
17930
17931     // private
17932     createCB : function(c){
17933         var el = this;
17934         return function(){
17935             el.constrainXY();
17936             el.sync(true);
17937             if(c){
17938                 c();
17939             }
17940         };
17941     },
17942
17943     // overridden Element method
17944     setX : function(x, a, d, c, e){
17945         this.setXY([x, this.getY()], a, d, c, e);
17946     },
17947
17948     // overridden Element method
17949     setY : function(y, a, d, c, e){
17950         this.setXY([this.getX(), y], a, d, c, e);
17951     },
17952
17953     // overridden Element method
17954     setSize : function(w, h, a, d, c, e){
17955         this.beforeAction();
17956         var cb = this.createCB(c);
17957         supr.setSize.call(this, w, h, a, d, cb, e);
17958         if(!a){
17959             cb();
17960         }
17961     },
17962
17963     // overridden Element method
17964     setWidth : function(w, a, d, c, e){
17965         this.beforeAction();
17966         var cb = this.createCB(c);
17967         supr.setWidth.call(this, w, a, d, cb, e);
17968         if(!a){
17969             cb();
17970         }
17971     },
17972
17973     // overridden Element method
17974     setHeight : function(h, a, d, c, e){
17975         this.beforeAction();
17976         var cb = this.createCB(c);
17977         supr.setHeight.call(this, h, a, d, cb, e);
17978         if(!a){
17979             cb();
17980         }
17981     },
17982
17983     // overridden Element method
17984     setBounds : function(x, y, w, h, a, d, c, e){
17985         this.beforeAction();
17986         var cb = this.createCB(c);
17987         if(!a){
17988             this.storeXY([x, y]);
17989             supr.setXY.call(this, [x, y]);
17990             supr.setSize.call(this, w, h, a, d, cb, e);
17991             cb();
17992         }else{
17993             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17994         }
17995         return this;
17996     },
17997     
17998     /**
17999      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18000      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18001      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18002      * @param {Number} zindex The new z-index to set
18003      * @return {this} The Layer
18004      */
18005     setZIndex : function(zindex){
18006         this.zindex = zindex;
18007         this.setStyle("z-index", zindex + 2);
18008         if(this.shadow){
18009             this.shadow.setZIndex(zindex + 1);
18010         }
18011         if(this.shim){
18012             this.shim.setStyle("z-index", zindex);
18013         }
18014     }
18015 });
18016 })();/*
18017  * Original code for Roojs - LGPL
18018  * <script type="text/javascript">
18019  */
18020  
18021 /**
18022  * @class Roo.XComponent
18023  * A delayed Element creator...
18024  * Or a way to group chunks of interface together.
18025  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18026  *  used in conjunction with XComponent.build() it will create an instance of each element,
18027  *  then call addxtype() to build the User interface.
18028  * 
18029  * Mypart.xyx = new Roo.XComponent({
18030
18031     parent : 'Mypart.xyz', // empty == document.element.!!
18032     order : '001',
18033     name : 'xxxx'
18034     region : 'xxxx'
18035     disabled : function() {} 
18036      
18037     tree : function() { // return an tree of xtype declared components
18038         var MODULE = this;
18039         return 
18040         {
18041             xtype : 'NestedLayoutPanel',
18042             // technicall
18043         }
18044      ]
18045  *})
18046  *
18047  *
18048  * It can be used to build a big heiracy, with parent etc.
18049  * or you can just use this to render a single compoent to a dom element
18050  * MYPART.render(Roo.Element | String(id) | dom_element )
18051  *
18052  *
18053  * Usage patterns.
18054  *
18055  * Classic Roo
18056  *
18057  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18058  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18059  *
18060  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18061  *
18062  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18063  * - if mulitple topModules exist, the last one is defined as the top module.
18064  *
18065  * Embeded Roo
18066  * 
18067  * When the top level or multiple modules are to embedded into a existing HTML page,
18068  * the parent element can container '#id' of the element where the module will be drawn.
18069  *
18070  * Bootstrap Roo
18071  *
18072  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18073  * it relies more on a include mechanism, where sub modules are included into an outer page.
18074  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18075  * 
18076  * Bootstrap Roo Included elements
18077  *
18078  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18079  * hence confusing the component builder as it thinks there are multiple top level elements. 
18080  *
18081  * String Over-ride & Translations
18082  *
18083  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18084  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18085  * are needed. @see Roo.XComponent.overlayString  
18086  * 
18087  * 
18088  * 
18089  * @extends Roo.util.Observable
18090  * @constructor
18091  * @param cfg {Object} configuration of component
18092  * 
18093  */
18094 Roo.XComponent = function(cfg) {
18095     Roo.apply(this, cfg);
18096     this.addEvents({ 
18097         /**
18098              * @event built
18099              * Fires when this the componnt is built
18100              * @param {Roo.XComponent} c the component
18101              */
18102         'built' : true
18103         
18104     });
18105     this.region = this.region || 'center'; // default..
18106     Roo.XComponent.register(this);
18107     this.modules = false;
18108     this.el = false; // where the layout goes..
18109     
18110     
18111 }
18112 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18113     /**
18114      * @property el
18115      * The created element (with Roo.factory())
18116      * @type {Roo.Layout}
18117      */
18118     el  : false,
18119     
18120     /**
18121      * @property el
18122      * for BC  - use el in new code
18123      * @type {Roo.Layout}
18124      */
18125     panel : false,
18126     
18127     /**
18128      * @property layout
18129      * for BC  - use el in new code
18130      * @type {Roo.Layout}
18131      */
18132     layout : false,
18133     
18134      /**
18135      * @cfg {Function|boolean} disabled
18136      * If this module is disabled by some rule, return true from the funtion
18137      */
18138     disabled : false,
18139     
18140     /**
18141      * @cfg {String} parent 
18142      * Name of parent element which it get xtype added to..
18143      */
18144     parent: false,
18145     
18146     /**
18147      * @cfg {String} order
18148      * Used to set the order in which elements are created (usefull for multiple tabs)
18149      */
18150     
18151     order : false,
18152     /**
18153      * @cfg {String} name
18154      * String to display while loading.
18155      */
18156     name : false,
18157     /**
18158      * @cfg {String} region
18159      * Region to render component to (defaults to center)
18160      */
18161     region : 'center',
18162     
18163     /**
18164      * @cfg {Array} items
18165      * A single item array - the first element is the root of the tree..
18166      * It's done this way to stay compatible with the Xtype system...
18167      */
18168     items : false,
18169     
18170     /**
18171      * @property _tree
18172      * The method that retuns the tree of parts that make up this compoennt 
18173      * @type {function}
18174      */
18175     _tree  : false,
18176     
18177      /**
18178      * render
18179      * render element to dom or tree
18180      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18181      */
18182     
18183     render : function(el)
18184     {
18185         
18186         el = el || false;
18187         var hp = this.parent ? 1 : 0;
18188         Roo.debug &&  Roo.log(this);
18189         
18190         var tree = this._tree ? this._tree() : this.tree();
18191
18192         
18193         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18194             // if parent is a '#.....' string, then let's use that..
18195             var ename = this.parent.substr(1);
18196             this.parent = false;
18197             Roo.debug && Roo.log(ename);
18198             switch (ename) {
18199                 case 'bootstrap-body':
18200                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18201                         // this is the BorderLayout standard?
18202                        this.parent = { el : true };
18203                        break;
18204                     }
18205                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18206                         // need to insert stuff...
18207                         this.parent =  {
18208                              el : new Roo.bootstrap.layout.Border({
18209                                  el : document.body, 
18210                      
18211                                  center: {
18212                                     titlebar: false,
18213                                     autoScroll:false,
18214                                     closeOnTab: true,
18215                                     tabPosition: 'top',
18216                                       //resizeTabs: true,
18217                                     alwaysShowTabs: true,
18218                                     hideTabs: false
18219                                      //minTabWidth: 140
18220                                  }
18221                              })
18222                         
18223                          };
18224                          break;
18225                     }
18226                          
18227                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18228                         this.parent = { el :  new  Roo.bootstrap.Body() };
18229                         Roo.debug && Roo.log("setting el to doc body");
18230                          
18231                     } else {
18232                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18233                     }
18234                     break;
18235                 case 'bootstrap':
18236                     this.parent = { el : true};
18237                     // fall through
18238                 default:
18239                     el = Roo.get(ename);
18240                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18241                         this.parent = { el : true};
18242                     }
18243                     
18244                     break;
18245             }
18246                 
18247             
18248             if (!el && !this.parent) {
18249                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18250                 return;
18251             }
18252         }
18253         
18254         Roo.debug && Roo.log("EL:");
18255         Roo.debug && Roo.log(el);
18256         Roo.debug && Roo.log("this.parent.el:");
18257         Roo.debug && Roo.log(this.parent.el);
18258         
18259
18260         // altertive root elements ??? - we need a better way to indicate these.
18261         var is_alt = Roo.XComponent.is_alt ||
18262                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18263                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18264                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18265         
18266         
18267         
18268         if (!this.parent && is_alt) {
18269             //el = Roo.get(document.body);
18270             this.parent = { el : true };
18271         }
18272             
18273             
18274         
18275         if (!this.parent) {
18276             
18277             Roo.debug && Roo.log("no parent - creating one");
18278             
18279             el = el ? Roo.get(el) : false;      
18280             
18281             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18282                 
18283                 this.parent =  {
18284                     el : new Roo.bootstrap.layout.Border({
18285                         el: el || document.body,
18286                     
18287                         center: {
18288                             titlebar: false,
18289                             autoScroll:false,
18290                             closeOnTab: true,
18291                             tabPosition: 'top',
18292                              //resizeTabs: true,
18293                             alwaysShowTabs: false,
18294                             hideTabs: true,
18295                             minTabWidth: 140,
18296                             overflow: 'visible'
18297                          }
18298                      })
18299                 };
18300             } else {
18301             
18302                 // it's a top level one..
18303                 this.parent =  {
18304                     el : new Roo.BorderLayout(el || document.body, {
18305                         center: {
18306                             titlebar: false,
18307                             autoScroll:false,
18308                             closeOnTab: true,
18309                             tabPosition: 'top',
18310                              //resizeTabs: true,
18311                             alwaysShowTabs: el && hp? false :  true,
18312                             hideTabs: el || !hp ? true :  false,
18313                             minTabWidth: 140
18314                          }
18315                     })
18316                 };
18317             }
18318         }
18319         
18320         if (!this.parent.el) {
18321                 // probably an old style ctor, which has been disabled.
18322                 return;
18323
18324         }
18325                 // The 'tree' method is  '_tree now' 
18326             
18327         tree.region = tree.region || this.region;
18328         var is_body = false;
18329         if (this.parent.el === true) {
18330             // bootstrap... - body..
18331             if (el) {
18332                 tree.el = el;
18333             }
18334             this.parent.el = Roo.factory(tree);
18335             is_body = true;
18336         }
18337         
18338         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18339         this.fireEvent('built', this);
18340         
18341         this.panel = this.el;
18342         this.layout = this.panel.layout;
18343         this.parentLayout = this.parent.layout  || false;  
18344          
18345     }
18346     
18347 });
18348
18349 Roo.apply(Roo.XComponent, {
18350     /**
18351      * @property  hideProgress
18352      * true to disable the building progress bar.. usefull on single page renders.
18353      * @type Boolean
18354      */
18355     hideProgress : false,
18356     /**
18357      * @property  buildCompleted
18358      * True when the builder has completed building the interface.
18359      * @type Boolean
18360      */
18361     buildCompleted : false,
18362      
18363     /**
18364      * @property  topModule
18365      * the upper most module - uses document.element as it's constructor.
18366      * @type Object
18367      */
18368      
18369     topModule  : false,
18370       
18371     /**
18372      * @property  modules
18373      * array of modules to be created by registration system.
18374      * @type {Array} of Roo.XComponent
18375      */
18376     
18377     modules : [],
18378     /**
18379      * @property  elmodules
18380      * array of modules to be created by which use #ID 
18381      * @type {Array} of Roo.XComponent
18382      */
18383      
18384     elmodules : [],
18385
18386      /**
18387      * @property  is_alt
18388      * Is an alternative Root - normally used by bootstrap or other systems,
18389      *    where the top element in the tree can wrap 'body' 
18390      * @type {boolean}  (default false)
18391      */
18392      
18393     is_alt : false,
18394     /**
18395      * @property  build_from_html
18396      * Build elements from html - used by bootstrap HTML stuff 
18397      *    - this is cleared after build is completed
18398      * @type {boolean}    (default false)
18399      */
18400      
18401     build_from_html : false,
18402     /**
18403      * Register components to be built later.
18404      *
18405      * This solves the following issues
18406      * - Building is not done on page load, but after an authentication process has occured.
18407      * - Interface elements are registered on page load
18408      * - Parent Interface elements may not be loaded before child, so this handles that..
18409      * 
18410      *
18411      * example:
18412      * 
18413      * MyApp.register({
18414           order : '000001',
18415           module : 'Pman.Tab.projectMgr',
18416           region : 'center',
18417           parent : 'Pman.layout',
18418           disabled : false,  // or use a function..
18419         })
18420      
18421      * * @param {Object} details about module
18422      */
18423     register : function(obj) {
18424                 
18425         Roo.XComponent.event.fireEvent('register', obj);
18426         switch(typeof(obj.disabled) ) {
18427                 
18428             case 'undefined':
18429                 break;
18430             
18431             case 'function':
18432                 if ( obj.disabled() ) {
18433                         return;
18434                 }
18435                 break;
18436             
18437             default:
18438                 if (obj.disabled || obj.region == '#disabled') {
18439                         return;
18440                 }
18441                 break;
18442         }
18443                 
18444         this.modules.push(obj);
18445          
18446     },
18447     /**
18448      * convert a string to an object..
18449      * eg. 'AAA.BBB' -> finds AAA.BBB
18450
18451      */
18452     
18453     toObject : function(str)
18454     {
18455         if (!str || typeof(str) == 'object') {
18456             return str;
18457         }
18458         if (str.substring(0,1) == '#') {
18459             return str;
18460         }
18461
18462         var ar = str.split('.');
18463         var rt, o;
18464         rt = ar.shift();
18465             /** eval:var:o */
18466         try {
18467             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18468         } catch (e) {
18469             throw "Module not found : " + str;
18470         }
18471         
18472         if (o === false) {
18473             throw "Module not found : " + str;
18474         }
18475         Roo.each(ar, function(e) {
18476             if (typeof(o[e]) == 'undefined') {
18477                 throw "Module not found : " + str;
18478             }
18479             o = o[e];
18480         });
18481         
18482         return o;
18483         
18484     },
18485     
18486     
18487     /**
18488      * move modules into their correct place in the tree..
18489      * 
18490      */
18491     preBuild : function ()
18492     {
18493         var _t = this;
18494         Roo.each(this.modules , function (obj)
18495         {
18496             Roo.XComponent.event.fireEvent('beforebuild', obj);
18497             
18498             var opar = obj.parent;
18499             try { 
18500                 obj.parent = this.toObject(opar);
18501             } catch(e) {
18502                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18503                 return;
18504             }
18505             
18506             if (!obj.parent) {
18507                 Roo.debug && Roo.log("GOT top level module");
18508                 Roo.debug && Roo.log(obj);
18509                 obj.modules = new Roo.util.MixedCollection(false, 
18510                     function(o) { return o.order + '' }
18511                 );
18512                 this.topModule = obj;
18513                 return;
18514             }
18515                         // parent is a string (usually a dom element name..)
18516             if (typeof(obj.parent) == 'string') {
18517                 this.elmodules.push(obj);
18518                 return;
18519             }
18520             if (obj.parent.constructor != Roo.XComponent) {
18521                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18522             }
18523             if (!obj.parent.modules) {
18524                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18525                     function(o) { return o.order + '' }
18526                 );
18527             }
18528             if (obj.parent.disabled) {
18529                 obj.disabled = true;
18530             }
18531             obj.parent.modules.add(obj);
18532         }, this);
18533     },
18534     
18535      /**
18536      * make a list of modules to build.
18537      * @return {Array} list of modules. 
18538      */ 
18539     
18540     buildOrder : function()
18541     {
18542         var _this = this;
18543         var cmp = function(a,b) {   
18544             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18545         };
18546         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18547             throw "No top level modules to build";
18548         }
18549         
18550         // make a flat list in order of modules to build.
18551         var mods = this.topModule ? [ this.topModule ] : [];
18552                 
18553         
18554         // elmodules (is a list of DOM based modules )
18555         Roo.each(this.elmodules, function(e) {
18556             mods.push(e);
18557             if (!this.topModule &&
18558                 typeof(e.parent) == 'string' &&
18559                 e.parent.substring(0,1) == '#' &&
18560                 Roo.get(e.parent.substr(1))
18561                ) {
18562                 
18563                 _this.topModule = e;
18564             }
18565             
18566         });
18567
18568         
18569         // add modules to their parents..
18570         var addMod = function(m) {
18571             Roo.debug && Roo.log("build Order: add: " + m.name);
18572                 
18573             mods.push(m);
18574             if (m.modules && !m.disabled) {
18575                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18576                 m.modules.keySort('ASC',  cmp );
18577                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18578     
18579                 m.modules.each(addMod);
18580             } else {
18581                 Roo.debug && Roo.log("build Order: no child modules");
18582             }
18583             // not sure if this is used any more..
18584             if (m.finalize) {
18585                 m.finalize.name = m.name + " (clean up) ";
18586                 mods.push(m.finalize);
18587             }
18588             
18589         }
18590         if (this.topModule && this.topModule.modules) { 
18591             this.topModule.modules.keySort('ASC',  cmp );
18592             this.topModule.modules.each(addMod);
18593         } 
18594         return mods;
18595     },
18596     
18597      /**
18598      * Build the registered modules.
18599      * @param {Object} parent element.
18600      * @param {Function} optional method to call after module has been added.
18601      * 
18602      */ 
18603    
18604     build : function(opts) 
18605     {
18606         
18607         if (typeof(opts) != 'undefined') {
18608             Roo.apply(this,opts);
18609         }
18610         
18611         this.preBuild();
18612         var mods = this.buildOrder();
18613       
18614         //this.allmods = mods;
18615         //Roo.debug && Roo.log(mods);
18616         //return;
18617         if (!mods.length) { // should not happen
18618             throw "NO modules!!!";
18619         }
18620         
18621         
18622         var msg = "Building Interface...";
18623         // flash it up as modal - so we store the mask!?
18624         if (!this.hideProgress && Roo.MessageBox) {
18625             Roo.MessageBox.show({ title: 'loading' });
18626             Roo.MessageBox.show({
18627                title: "Please wait...",
18628                msg: msg,
18629                width:450,
18630                progress:true,
18631                buttons : false,
18632                closable:false,
18633                modal: false
18634               
18635             });
18636         }
18637         var total = mods.length;
18638         
18639         var _this = this;
18640         var progressRun = function() {
18641             if (!mods.length) {
18642                 Roo.debug && Roo.log('hide?');
18643                 if (!this.hideProgress && Roo.MessageBox) {
18644                     Roo.MessageBox.hide();
18645                 }
18646                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18647                 
18648                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18649                 
18650                 // THE END...
18651                 return false;   
18652             }
18653             
18654             var m = mods.shift();
18655             
18656             
18657             Roo.debug && Roo.log(m);
18658             // not sure if this is supported any more.. - modules that are are just function
18659             if (typeof(m) == 'function') { 
18660                 m.call(this);
18661                 return progressRun.defer(10, _this);
18662             } 
18663             
18664             
18665             msg = "Building Interface " + (total  - mods.length) + 
18666                     " of " + total + 
18667                     (m.name ? (' - ' + m.name) : '');
18668                         Roo.debug && Roo.log(msg);
18669             if (!_this.hideProgress &&  Roo.MessageBox) { 
18670                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18671             }
18672             
18673          
18674             // is the module disabled?
18675             var disabled = (typeof(m.disabled) == 'function') ?
18676                 m.disabled.call(m.module.disabled) : m.disabled;    
18677             
18678             
18679             if (disabled) {
18680                 return progressRun(); // we do not update the display!
18681             }
18682             
18683             // now build 
18684             
18685                         
18686                         
18687             m.render();
18688             // it's 10 on top level, and 1 on others??? why...
18689             return progressRun.defer(10, _this);
18690              
18691         }
18692         progressRun.defer(1, _this);
18693      
18694         
18695         
18696     },
18697     /**
18698      * Overlay a set of modified strings onto a component
18699      * This is dependant on our builder exporting the strings and 'named strings' elements.
18700      * 
18701      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18702      * @param {Object} associative array of 'named' string and it's new value.
18703      * 
18704      */
18705         overlayStrings : function( component, strings )
18706     {
18707         if (typeof(component['_named_strings']) == 'undefined') {
18708             throw "ERROR: component does not have _named_strings";
18709         }
18710         for ( var k in strings ) {
18711             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18712             if (md !== false) {
18713                 component['_strings'][md] = strings[k];
18714             } else {
18715                 Roo.log('could not find named string: ' + k + ' in');
18716                 Roo.log(component);
18717             }
18718             
18719         }
18720         
18721     },
18722     
18723         
18724         /**
18725          * Event Object.
18726          *
18727          *
18728          */
18729         event: false, 
18730     /**
18731          * wrapper for event.on - aliased later..  
18732          * Typically use to register a event handler for register:
18733          *
18734          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18735          *
18736          */
18737     on : false
18738    
18739     
18740     
18741 });
18742
18743 Roo.XComponent.event = new Roo.util.Observable({
18744                 events : { 
18745                         /**
18746                          * @event register
18747                          * Fires when an Component is registered,
18748                          * set the disable property on the Component to stop registration.
18749                          * @param {Roo.XComponent} c the component being registerd.
18750                          * 
18751                          */
18752                         'register' : true,
18753             /**
18754                          * @event beforebuild
18755                          * Fires before each Component is built
18756                          * can be used to apply permissions.
18757                          * @param {Roo.XComponent} c the component being registerd.
18758                          * 
18759                          */
18760                         'beforebuild' : true,
18761                         /**
18762                          * @event buildcomplete
18763                          * Fires on the top level element when all elements have been built
18764                          * @param {Roo.XComponent} the top level component.
18765                          */
18766                         'buildcomplete' : true
18767                         
18768                 }
18769 });
18770
18771 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18772  //
18773  /**
18774  * marked - a markdown parser
18775  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18776  * https://github.com/chjj/marked
18777  */
18778
18779
18780 /**
18781  *
18782  * Roo.Markdown - is a very crude wrapper around marked..
18783  *
18784  * usage:
18785  * 
18786  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18787  * 
18788  * Note: move the sample code to the bottom of this
18789  * file before uncommenting it.
18790  *
18791  */
18792
18793 Roo.Markdown = {};
18794 Roo.Markdown.toHtml = function(text) {
18795     
18796     var c = new Roo.Markdown.marked.setOptions({
18797             renderer: new Roo.Markdown.marked.Renderer(),
18798             gfm: true,
18799             tables: true,
18800             breaks: false,
18801             pedantic: false,
18802             sanitize: false,
18803             smartLists: true,
18804             smartypants: false
18805           });
18806     // A FEW HACKS!!?
18807     
18808     text = text.replace(/\\\n/g,' ');
18809     return Roo.Markdown.marked(text);
18810 };
18811 //
18812 // converter
18813 //
18814 // Wraps all "globals" so that the only thing
18815 // exposed is makeHtml().
18816 //
18817 (function() {
18818     
18819      /**
18820          * eval:var:escape
18821          * eval:var:unescape
18822          * eval:var:replace
18823          */
18824       
18825     /**
18826      * Helpers
18827      */
18828     
18829     var escape = function (html, encode) {
18830       return html
18831         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18832         .replace(/</g, '&lt;')
18833         .replace(/>/g, '&gt;')
18834         .replace(/"/g, '&quot;')
18835         .replace(/'/g, '&#39;');
18836     }
18837     
18838     var unescape = function (html) {
18839         // explicitly match decimal, hex, and named HTML entities 
18840       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18841         n = n.toLowerCase();
18842         if (n === 'colon') { return ':'; }
18843         if (n.charAt(0) === '#') {
18844           return n.charAt(1) === 'x'
18845             ? String.fromCharCode(parseInt(n.substring(2), 16))
18846             : String.fromCharCode(+n.substring(1));
18847         }
18848         return '';
18849       });
18850     }
18851     
18852     var replace = function (regex, opt) {
18853       regex = regex.source;
18854       opt = opt || '';
18855       return function self(name, val) {
18856         if (!name) { return new RegExp(regex, opt); }
18857         val = val.source || val;
18858         val = val.replace(/(^|[^\[])\^/g, '$1');
18859         regex = regex.replace(name, val);
18860         return self;
18861       };
18862     }
18863
18864
18865          /**
18866          * eval:var:noop
18867     */
18868     var noop = function () {}
18869     noop.exec = noop;
18870     
18871          /**
18872          * eval:var:merge
18873     */
18874     var merge = function (obj) {
18875       var i = 1
18876         , target
18877         , key;
18878     
18879       for (; i < arguments.length; i++) {
18880         target = arguments[i];
18881         for (key in target) {
18882           if (Object.prototype.hasOwnProperty.call(target, key)) {
18883             obj[key] = target[key];
18884           }
18885         }
18886       }
18887     
18888       return obj;
18889     }
18890     
18891     
18892     /**
18893      * Block-Level Grammar
18894      */
18895     
18896     
18897     
18898     
18899     var block = {
18900       newline: /^\n+/,
18901       code: /^( {4}[^\n]+\n*)+/,
18902       fences: noop,
18903       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18904       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18905       nptable: noop,
18906       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18907       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18908       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18909       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18910       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18911       table: noop,
18912       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18913       text: /^[^\n]+/
18914     };
18915     
18916     block.bullet = /(?:[*+-]|\d+\.)/;
18917     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18918     block.item = replace(block.item, 'gm')
18919       (/bull/g, block.bullet)
18920       ();
18921     
18922     block.list = replace(block.list)
18923       (/bull/g, block.bullet)
18924       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18925       ('def', '\\n+(?=' + block.def.source + ')')
18926       ();
18927     
18928     block.blockquote = replace(block.blockquote)
18929       ('def', block.def)
18930       ();
18931     
18932     block._tag = '(?!(?:'
18933       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18934       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18935       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18936     
18937     block.html = replace(block.html)
18938       ('comment', /<!--[\s\S]*?-->/)
18939       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18940       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18941       (/tag/g, block._tag)
18942       ();
18943     
18944     block.paragraph = replace(block.paragraph)
18945       ('hr', block.hr)
18946       ('heading', block.heading)
18947       ('lheading', block.lheading)
18948       ('blockquote', block.blockquote)
18949       ('tag', '<' + block._tag)
18950       ('def', block.def)
18951       ();
18952     
18953     /**
18954      * Normal Block Grammar
18955      */
18956     
18957     block.normal = merge({}, block);
18958     
18959     /**
18960      * GFM Block Grammar
18961      */
18962     
18963     block.gfm = merge({}, block.normal, {
18964       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18965       paragraph: /^/,
18966       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18967     });
18968     
18969     block.gfm.paragraph = replace(block.paragraph)
18970       ('(?!', '(?!'
18971         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18972         + block.list.source.replace('\\1', '\\3') + '|')
18973       ();
18974     
18975     /**
18976      * GFM + Tables Block Grammar
18977      */
18978     
18979     block.tables = merge({}, block.gfm, {
18980       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18981       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18982     });
18983     
18984     /**
18985      * Block Lexer
18986      */
18987     
18988     var Lexer = function (options) {
18989       this.tokens = [];
18990       this.tokens.links = {};
18991       this.options = options || marked.defaults;
18992       this.rules = block.normal;
18993     
18994       if (this.options.gfm) {
18995         if (this.options.tables) {
18996           this.rules = block.tables;
18997         } else {
18998           this.rules = block.gfm;
18999         }
19000       }
19001     }
19002     
19003     /**
19004      * Expose Block Rules
19005      */
19006     
19007     Lexer.rules = block;
19008     
19009     /**
19010      * Static Lex Method
19011      */
19012     
19013     Lexer.lex = function(src, options) {
19014       var lexer = new Lexer(options);
19015       return lexer.lex(src);
19016     };
19017     
19018     /**
19019      * Preprocessing
19020      */
19021     
19022     Lexer.prototype.lex = function(src) {
19023       src = src
19024         .replace(/\r\n|\r/g, '\n')
19025         .replace(/\t/g, '    ')
19026         .replace(/\u00a0/g, ' ')
19027         .replace(/\u2424/g, '\n');
19028     
19029       return this.token(src, true);
19030     };
19031     
19032     /**
19033      * Lexing
19034      */
19035     
19036     Lexer.prototype.token = function(src, top, bq) {
19037       var src = src.replace(/^ +$/gm, '')
19038         , next
19039         , loose
19040         , cap
19041         , bull
19042         , b
19043         , item
19044         , space
19045         , i
19046         , l;
19047     
19048       while (src) {
19049         // newline
19050         if (cap = this.rules.newline.exec(src)) {
19051           src = src.substring(cap[0].length);
19052           if (cap[0].length > 1) {
19053             this.tokens.push({
19054               type: 'space'
19055             });
19056           }
19057         }
19058     
19059         // code
19060         if (cap = this.rules.code.exec(src)) {
19061           src = src.substring(cap[0].length);
19062           cap = cap[0].replace(/^ {4}/gm, '');
19063           this.tokens.push({
19064             type: 'code',
19065             text: !this.options.pedantic
19066               ? cap.replace(/\n+$/, '')
19067               : cap
19068           });
19069           continue;
19070         }
19071     
19072         // fences (gfm)
19073         if (cap = this.rules.fences.exec(src)) {
19074           src = src.substring(cap[0].length);
19075           this.tokens.push({
19076             type: 'code',
19077             lang: cap[2],
19078             text: cap[3] || ''
19079           });
19080           continue;
19081         }
19082     
19083         // heading
19084         if (cap = this.rules.heading.exec(src)) {
19085           src = src.substring(cap[0].length);
19086           this.tokens.push({
19087             type: 'heading',
19088             depth: cap[1].length,
19089             text: cap[2]
19090           });
19091           continue;
19092         }
19093     
19094         // table no leading pipe (gfm)
19095         if (top && (cap = this.rules.nptable.exec(src))) {
19096           src = src.substring(cap[0].length);
19097     
19098           item = {
19099             type: 'table',
19100             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19101             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19102             cells: cap[3].replace(/\n$/, '').split('\n')
19103           };
19104     
19105           for (i = 0; i < item.align.length; i++) {
19106             if (/^ *-+: *$/.test(item.align[i])) {
19107               item.align[i] = 'right';
19108             } else if (/^ *:-+: *$/.test(item.align[i])) {
19109               item.align[i] = 'center';
19110             } else if (/^ *:-+ *$/.test(item.align[i])) {
19111               item.align[i] = 'left';
19112             } else {
19113               item.align[i] = null;
19114             }
19115           }
19116     
19117           for (i = 0; i < item.cells.length; i++) {
19118             item.cells[i] = item.cells[i].split(/ *\| */);
19119           }
19120     
19121           this.tokens.push(item);
19122     
19123           continue;
19124         }
19125     
19126         // lheading
19127         if (cap = this.rules.lheading.exec(src)) {
19128           src = src.substring(cap[0].length);
19129           this.tokens.push({
19130             type: 'heading',
19131             depth: cap[2] === '=' ? 1 : 2,
19132             text: cap[1]
19133           });
19134           continue;
19135         }
19136     
19137         // hr
19138         if (cap = this.rules.hr.exec(src)) {
19139           src = src.substring(cap[0].length);
19140           this.tokens.push({
19141             type: 'hr'
19142           });
19143           continue;
19144         }
19145     
19146         // blockquote
19147         if (cap = this.rules.blockquote.exec(src)) {
19148           src = src.substring(cap[0].length);
19149     
19150           this.tokens.push({
19151             type: 'blockquote_start'
19152           });
19153     
19154           cap = cap[0].replace(/^ *> ?/gm, '');
19155     
19156           // Pass `top` to keep the current
19157           // "toplevel" state. This is exactly
19158           // how markdown.pl works.
19159           this.token(cap, top, true);
19160     
19161           this.tokens.push({
19162             type: 'blockquote_end'
19163           });
19164     
19165           continue;
19166         }
19167     
19168         // list
19169         if (cap = this.rules.list.exec(src)) {
19170           src = src.substring(cap[0].length);
19171           bull = cap[2];
19172     
19173           this.tokens.push({
19174             type: 'list_start',
19175             ordered: bull.length > 1
19176           });
19177     
19178           // Get each top-level item.
19179           cap = cap[0].match(this.rules.item);
19180     
19181           next = false;
19182           l = cap.length;
19183           i = 0;
19184     
19185           for (; i < l; i++) {
19186             item = cap[i];
19187     
19188             // Remove the list item's bullet
19189             // so it is seen as the next token.
19190             space = item.length;
19191             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19192     
19193             // Outdent whatever the
19194             // list item contains. Hacky.
19195             if (~item.indexOf('\n ')) {
19196               space -= item.length;
19197               item = !this.options.pedantic
19198                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19199                 : item.replace(/^ {1,4}/gm, '');
19200             }
19201     
19202             // Determine whether the next list item belongs here.
19203             // Backpedal if it does not belong in this list.
19204             if (this.options.smartLists && i !== l - 1) {
19205               b = block.bullet.exec(cap[i + 1])[0];
19206               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19207                 src = cap.slice(i + 1).join('\n') + src;
19208                 i = l - 1;
19209               }
19210             }
19211     
19212             // Determine whether item is loose or not.
19213             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19214             // for discount behavior.
19215             loose = next || /\n\n(?!\s*$)/.test(item);
19216             if (i !== l - 1) {
19217               next = item.charAt(item.length - 1) === '\n';
19218               if (!loose) { loose = next; }
19219             }
19220     
19221             this.tokens.push({
19222               type: loose
19223                 ? 'loose_item_start'
19224                 : 'list_item_start'
19225             });
19226     
19227             // Recurse.
19228             this.token(item, false, bq);
19229     
19230             this.tokens.push({
19231               type: 'list_item_end'
19232             });
19233           }
19234     
19235           this.tokens.push({
19236             type: 'list_end'
19237           });
19238     
19239           continue;
19240         }
19241     
19242         // html
19243         if (cap = this.rules.html.exec(src)) {
19244           src = src.substring(cap[0].length);
19245           this.tokens.push({
19246             type: this.options.sanitize
19247               ? 'paragraph'
19248               : 'html',
19249             pre: !this.options.sanitizer
19250               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19251             text: cap[0]
19252           });
19253           continue;
19254         }
19255     
19256         // def
19257         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19258           src = src.substring(cap[0].length);
19259           this.tokens.links[cap[1].toLowerCase()] = {
19260             href: cap[2],
19261             title: cap[3]
19262           };
19263           continue;
19264         }
19265     
19266         // table (gfm)
19267         if (top && (cap = this.rules.table.exec(src))) {
19268           src = src.substring(cap[0].length);
19269     
19270           item = {
19271             type: 'table',
19272             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19273             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19274             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19275           };
19276     
19277           for (i = 0; i < item.align.length; i++) {
19278             if (/^ *-+: *$/.test(item.align[i])) {
19279               item.align[i] = 'right';
19280             } else if (/^ *:-+: *$/.test(item.align[i])) {
19281               item.align[i] = 'center';
19282             } else if (/^ *:-+ *$/.test(item.align[i])) {
19283               item.align[i] = 'left';
19284             } else {
19285               item.align[i] = null;
19286             }
19287           }
19288     
19289           for (i = 0; i < item.cells.length; i++) {
19290             item.cells[i] = item.cells[i]
19291               .replace(/^ *\| *| *\| *$/g, '')
19292               .split(/ *\| */);
19293           }
19294     
19295           this.tokens.push(item);
19296     
19297           continue;
19298         }
19299     
19300         // top-level paragraph
19301         if (top && (cap = this.rules.paragraph.exec(src))) {
19302           src = src.substring(cap[0].length);
19303           this.tokens.push({
19304             type: 'paragraph',
19305             text: cap[1].charAt(cap[1].length - 1) === '\n'
19306               ? cap[1].slice(0, -1)
19307               : cap[1]
19308           });
19309           continue;
19310         }
19311     
19312         // text
19313         if (cap = this.rules.text.exec(src)) {
19314           // Top-level should never reach here.
19315           src = src.substring(cap[0].length);
19316           this.tokens.push({
19317             type: 'text',
19318             text: cap[0]
19319           });
19320           continue;
19321         }
19322     
19323         if (src) {
19324           throw new
19325             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19326         }
19327       }
19328     
19329       return this.tokens;
19330     };
19331     
19332     /**
19333      * Inline-Level Grammar
19334      */
19335     
19336     var inline = {
19337       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19338       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19339       url: noop,
19340       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19341       link: /^!?\[(inside)\]\(href\)/,
19342       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19343       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19344       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19345       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19346       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19347       br: /^ {2,}\n(?!\s*$)/,
19348       del: noop,
19349       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19350     };
19351     
19352     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19353     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19354     
19355     inline.link = replace(inline.link)
19356       ('inside', inline._inside)
19357       ('href', inline._href)
19358       ();
19359     
19360     inline.reflink = replace(inline.reflink)
19361       ('inside', inline._inside)
19362       ();
19363     
19364     /**
19365      * Normal Inline Grammar
19366      */
19367     
19368     inline.normal = merge({}, inline);
19369     
19370     /**
19371      * Pedantic Inline Grammar
19372      */
19373     
19374     inline.pedantic = merge({}, inline.normal, {
19375       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19376       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19377     });
19378     
19379     /**
19380      * GFM Inline Grammar
19381      */
19382     
19383     inline.gfm = merge({}, inline.normal, {
19384       escape: replace(inline.escape)('])', '~|])')(),
19385       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19386       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19387       text: replace(inline.text)
19388         (']|', '~]|')
19389         ('|', '|https?://|')
19390         ()
19391     });
19392     
19393     /**
19394      * GFM + Line Breaks Inline Grammar
19395      */
19396     
19397     inline.breaks = merge({}, inline.gfm, {
19398       br: replace(inline.br)('{2,}', '*')(),
19399       text: replace(inline.gfm.text)('{2,}', '*')()
19400     });
19401     
19402     /**
19403      * Inline Lexer & Compiler
19404      */
19405     
19406     var InlineLexer  = function (links, options) {
19407       this.options = options || marked.defaults;
19408       this.links = links;
19409       this.rules = inline.normal;
19410       this.renderer = this.options.renderer || new Renderer;
19411       this.renderer.options = this.options;
19412     
19413       if (!this.links) {
19414         throw new
19415           Error('Tokens array requires a `links` property.');
19416       }
19417     
19418       if (this.options.gfm) {
19419         if (this.options.breaks) {
19420           this.rules = inline.breaks;
19421         } else {
19422           this.rules = inline.gfm;
19423         }
19424       } else if (this.options.pedantic) {
19425         this.rules = inline.pedantic;
19426       }
19427     }
19428     
19429     /**
19430      * Expose Inline Rules
19431      */
19432     
19433     InlineLexer.rules = inline;
19434     
19435     /**
19436      * Static Lexing/Compiling Method
19437      */
19438     
19439     InlineLexer.output = function(src, links, options) {
19440       var inline = new InlineLexer(links, options);
19441       return inline.output(src);
19442     };
19443     
19444     /**
19445      * Lexing/Compiling
19446      */
19447     
19448     InlineLexer.prototype.output = function(src) {
19449       var out = ''
19450         , link
19451         , text
19452         , href
19453         , cap;
19454     
19455       while (src) {
19456         // escape
19457         if (cap = this.rules.escape.exec(src)) {
19458           src = src.substring(cap[0].length);
19459           out += cap[1];
19460           continue;
19461         }
19462     
19463         // autolink
19464         if (cap = this.rules.autolink.exec(src)) {
19465           src = src.substring(cap[0].length);
19466           if (cap[2] === '@') {
19467             text = cap[1].charAt(6) === ':'
19468               ? this.mangle(cap[1].substring(7))
19469               : this.mangle(cap[1]);
19470             href = this.mangle('mailto:') + text;
19471           } else {
19472             text = escape(cap[1]);
19473             href = text;
19474           }
19475           out += this.renderer.link(href, null, text);
19476           continue;
19477         }
19478     
19479         // url (gfm)
19480         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19481           src = src.substring(cap[0].length);
19482           text = escape(cap[1]);
19483           href = text;
19484           out += this.renderer.link(href, null, text);
19485           continue;
19486         }
19487     
19488         // tag
19489         if (cap = this.rules.tag.exec(src)) {
19490           if (!this.inLink && /^<a /i.test(cap[0])) {
19491             this.inLink = true;
19492           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19493             this.inLink = false;
19494           }
19495           src = src.substring(cap[0].length);
19496           out += this.options.sanitize
19497             ? this.options.sanitizer
19498               ? this.options.sanitizer(cap[0])
19499               : escape(cap[0])
19500             : cap[0];
19501           continue;
19502         }
19503     
19504         // link
19505         if (cap = this.rules.link.exec(src)) {
19506           src = src.substring(cap[0].length);
19507           this.inLink = true;
19508           out += this.outputLink(cap, {
19509             href: cap[2],
19510             title: cap[3]
19511           });
19512           this.inLink = false;
19513           continue;
19514         }
19515     
19516         // reflink, nolink
19517         if ((cap = this.rules.reflink.exec(src))
19518             || (cap = this.rules.nolink.exec(src))) {
19519           src = src.substring(cap[0].length);
19520           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19521           link = this.links[link.toLowerCase()];
19522           if (!link || !link.href) {
19523             out += cap[0].charAt(0);
19524             src = cap[0].substring(1) + src;
19525             continue;
19526           }
19527           this.inLink = true;
19528           out += this.outputLink(cap, link);
19529           this.inLink = false;
19530           continue;
19531         }
19532     
19533         // strong
19534         if (cap = this.rules.strong.exec(src)) {
19535           src = src.substring(cap[0].length);
19536           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19537           continue;
19538         }
19539     
19540         // em
19541         if (cap = this.rules.em.exec(src)) {
19542           src = src.substring(cap[0].length);
19543           out += this.renderer.em(this.output(cap[2] || cap[1]));
19544           continue;
19545         }
19546     
19547         // code
19548         if (cap = this.rules.code.exec(src)) {
19549           src = src.substring(cap[0].length);
19550           out += this.renderer.codespan(escape(cap[2], true));
19551           continue;
19552         }
19553     
19554         // br
19555         if (cap = this.rules.br.exec(src)) {
19556           src = src.substring(cap[0].length);
19557           out += this.renderer.br();
19558           continue;
19559         }
19560     
19561         // del (gfm)
19562         if (cap = this.rules.del.exec(src)) {
19563           src = src.substring(cap[0].length);
19564           out += this.renderer.del(this.output(cap[1]));
19565           continue;
19566         }
19567     
19568         // text
19569         if (cap = this.rules.text.exec(src)) {
19570           src = src.substring(cap[0].length);
19571           out += this.renderer.text(escape(this.smartypants(cap[0])));
19572           continue;
19573         }
19574     
19575         if (src) {
19576           throw new
19577             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19578         }
19579       }
19580     
19581       return out;
19582     };
19583     
19584     /**
19585      * Compile Link
19586      */
19587     
19588     InlineLexer.prototype.outputLink = function(cap, link) {
19589       var href = escape(link.href)
19590         , title = link.title ? escape(link.title) : null;
19591     
19592       return cap[0].charAt(0) !== '!'
19593         ? this.renderer.link(href, title, this.output(cap[1]))
19594         : this.renderer.image(href, title, escape(cap[1]));
19595     };
19596     
19597     /**
19598      * Smartypants Transformations
19599      */
19600     
19601     InlineLexer.prototype.smartypants = function(text) {
19602       if (!this.options.smartypants)  { return text; }
19603       return text
19604         // em-dashes
19605         .replace(/---/g, '\u2014')
19606         // en-dashes
19607         .replace(/--/g, '\u2013')
19608         // opening singles
19609         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19610         // closing singles & apostrophes
19611         .replace(/'/g, '\u2019')
19612         // opening doubles
19613         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19614         // closing doubles
19615         .replace(/"/g, '\u201d')
19616         // ellipses
19617         .replace(/\.{3}/g, '\u2026');
19618     };
19619     
19620     /**
19621      * Mangle Links
19622      */
19623     
19624     InlineLexer.prototype.mangle = function(text) {
19625       if (!this.options.mangle) { return text; }
19626       var out = ''
19627         , l = text.length
19628         , i = 0
19629         , ch;
19630     
19631       for (; i < l; i++) {
19632         ch = text.charCodeAt(i);
19633         if (Math.random() > 0.5) {
19634           ch = 'x' + ch.toString(16);
19635         }
19636         out += '&#' + ch + ';';
19637       }
19638     
19639       return out;
19640     };
19641     
19642     /**
19643      * Renderer
19644      */
19645     
19646      /**
19647          * eval:var:Renderer
19648     */
19649     
19650     var Renderer   = function (options) {
19651       this.options = options || {};
19652     }
19653     
19654     Renderer.prototype.code = function(code, lang, escaped) {
19655       if (this.options.highlight) {
19656         var out = this.options.highlight(code, lang);
19657         if (out != null && out !== code) {
19658           escaped = true;
19659           code = out;
19660         }
19661       } else {
19662             // hack!!! - it's already escapeD?
19663             escaped = true;
19664       }
19665     
19666       if (!lang) {
19667         return '<pre><code>'
19668           + (escaped ? code : escape(code, true))
19669           + '\n</code></pre>';
19670       }
19671     
19672       return '<pre><code class="'
19673         + this.options.langPrefix
19674         + escape(lang, true)
19675         + '">'
19676         + (escaped ? code : escape(code, true))
19677         + '\n</code></pre>\n';
19678     };
19679     
19680     Renderer.prototype.blockquote = function(quote) {
19681       return '<blockquote>\n' + quote + '</blockquote>\n';
19682     };
19683     
19684     Renderer.prototype.html = function(html) {
19685       return html;
19686     };
19687     
19688     Renderer.prototype.heading = function(text, level, raw) {
19689       return '<h'
19690         + level
19691         + ' id="'
19692         + this.options.headerPrefix
19693         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19694         + '">'
19695         + text
19696         + '</h'
19697         + level
19698         + '>\n';
19699     };
19700     
19701     Renderer.prototype.hr = function() {
19702       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19703     };
19704     
19705     Renderer.prototype.list = function(body, ordered) {
19706       var type = ordered ? 'ol' : 'ul';
19707       return '<' + type + '>\n' + body + '</' + type + '>\n';
19708     };
19709     
19710     Renderer.prototype.listitem = function(text) {
19711       return '<li>' + text + '</li>\n';
19712     };
19713     
19714     Renderer.prototype.paragraph = function(text) {
19715       return '<p>' + text + '</p>\n';
19716     };
19717     
19718     Renderer.prototype.table = function(header, body) {
19719       return '<table class="table table-striped">\n'
19720         + '<thead>\n'
19721         + header
19722         + '</thead>\n'
19723         + '<tbody>\n'
19724         + body
19725         + '</tbody>\n'
19726         + '</table>\n';
19727     };
19728     
19729     Renderer.prototype.tablerow = function(content) {
19730       return '<tr>\n' + content + '</tr>\n';
19731     };
19732     
19733     Renderer.prototype.tablecell = function(content, flags) {
19734       var type = flags.header ? 'th' : 'td';
19735       var tag = flags.align
19736         ? '<' + type + ' style="text-align:' + flags.align + '">'
19737         : '<' + type + '>';
19738       return tag + content + '</' + type + '>\n';
19739     };
19740     
19741     // span level renderer
19742     Renderer.prototype.strong = function(text) {
19743       return '<strong>' + text + '</strong>';
19744     };
19745     
19746     Renderer.prototype.em = function(text) {
19747       return '<em>' + text + '</em>';
19748     };
19749     
19750     Renderer.prototype.codespan = function(text) {
19751       return '<code>' + text + '</code>';
19752     };
19753     
19754     Renderer.prototype.br = function() {
19755       return this.options.xhtml ? '<br/>' : '<br>';
19756     };
19757     
19758     Renderer.prototype.del = function(text) {
19759       return '<del>' + text + '</del>';
19760     };
19761     
19762     Renderer.prototype.link = function(href, title, text) {
19763       if (this.options.sanitize) {
19764         try {
19765           var prot = decodeURIComponent(unescape(href))
19766             .replace(/[^\w:]/g, '')
19767             .toLowerCase();
19768         } catch (e) {
19769           return '';
19770         }
19771         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19772           return '';
19773         }
19774       }
19775       var out = '<a href="' + href + '"';
19776       if (title) {
19777         out += ' title="' + title + '"';
19778       }
19779       out += '>' + text + '</a>';
19780       return out;
19781     };
19782     
19783     Renderer.prototype.image = function(href, title, text) {
19784       var out = '<img src="' + href + '" alt="' + text + '"';
19785       if (title) {
19786         out += ' title="' + title + '"';
19787       }
19788       out += this.options.xhtml ? '/>' : '>';
19789       return out;
19790     };
19791     
19792     Renderer.prototype.text = function(text) {
19793       return text;
19794     };
19795     
19796     /**
19797      * Parsing & Compiling
19798      */
19799          /**
19800          * eval:var:Parser
19801     */
19802     
19803     var Parser= function (options) {
19804       this.tokens = [];
19805       this.token = null;
19806       this.options = options || marked.defaults;
19807       this.options.renderer = this.options.renderer || new Renderer;
19808       this.renderer = this.options.renderer;
19809       this.renderer.options = this.options;
19810     }
19811     
19812     /**
19813      * Static Parse Method
19814      */
19815     
19816     Parser.parse = function(src, options, renderer) {
19817       var parser = new Parser(options, renderer);
19818       return parser.parse(src);
19819     };
19820     
19821     /**
19822      * Parse Loop
19823      */
19824     
19825     Parser.prototype.parse = function(src) {
19826       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19827       this.tokens = src.reverse();
19828     
19829       var out = '';
19830       while (this.next()) {
19831         out += this.tok();
19832       }
19833     
19834       return out;
19835     };
19836     
19837     /**
19838      * Next Token
19839      */
19840     
19841     Parser.prototype.next = function() {
19842       return this.token = this.tokens.pop();
19843     };
19844     
19845     /**
19846      * Preview Next Token
19847      */
19848     
19849     Parser.prototype.peek = function() {
19850       return this.tokens[this.tokens.length - 1] || 0;
19851     };
19852     
19853     /**
19854      * Parse Text Tokens
19855      */
19856     
19857     Parser.prototype.parseText = function() {
19858       var body = this.token.text;
19859     
19860       while (this.peek().type === 'text') {
19861         body += '\n' + this.next().text;
19862       }
19863     
19864       return this.inline.output(body);
19865     };
19866     
19867     /**
19868      * Parse Current Token
19869      */
19870     
19871     Parser.prototype.tok = function() {
19872       switch (this.token.type) {
19873         case 'space': {
19874           return '';
19875         }
19876         case 'hr': {
19877           return this.renderer.hr();
19878         }
19879         case 'heading': {
19880           return this.renderer.heading(
19881             this.inline.output(this.token.text),
19882             this.token.depth,
19883             this.token.text);
19884         }
19885         case 'code': {
19886           return this.renderer.code(this.token.text,
19887             this.token.lang,
19888             this.token.escaped);
19889         }
19890         case 'table': {
19891           var header = ''
19892             , body = ''
19893             , i
19894             , row
19895             , cell
19896             , flags
19897             , j;
19898     
19899           // header
19900           cell = '';
19901           for (i = 0; i < this.token.header.length; i++) {
19902             flags = { header: true, align: this.token.align[i] };
19903             cell += this.renderer.tablecell(
19904               this.inline.output(this.token.header[i]),
19905               { header: true, align: this.token.align[i] }
19906             );
19907           }
19908           header += this.renderer.tablerow(cell);
19909     
19910           for (i = 0; i < this.token.cells.length; i++) {
19911             row = this.token.cells[i];
19912     
19913             cell = '';
19914             for (j = 0; j < row.length; j++) {
19915               cell += this.renderer.tablecell(
19916                 this.inline.output(row[j]),
19917                 { header: false, align: this.token.align[j] }
19918               );
19919             }
19920     
19921             body += this.renderer.tablerow(cell);
19922           }
19923           return this.renderer.table(header, body);
19924         }
19925         case 'blockquote_start': {
19926           var body = '';
19927     
19928           while (this.next().type !== 'blockquote_end') {
19929             body += this.tok();
19930           }
19931     
19932           return this.renderer.blockquote(body);
19933         }
19934         case 'list_start': {
19935           var body = ''
19936             , ordered = this.token.ordered;
19937     
19938           while (this.next().type !== 'list_end') {
19939             body += this.tok();
19940           }
19941     
19942           return this.renderer.list(body, ordered);
19943         }
19944         case 'list_item_start': {
19945           var body = '';
19946     
19947           while (this.next().type !== 'list_item_end') {
19948             body += this.token.type === 'text'
19949               ? this.parseText()
19950               : this.tok();
19951           }
19952     
19953           return this.renderer.listitem(body);
19954         }
19955         case 'loose_item_start': {
19956           var body = '';
19957     
19958           while (this.next().type !== 'list_item_end') {
19959             body += this.tok();
19960           }
19961     
19962           return this.renderer.listitem(body);
19963         }
19964         case 'html': {
19965           var html = !this.token.pre && !this.options.pedantic
19966             ? this.inline.output(this.token.text)
19967             : this.token.text;
19968           return this.renderer.html(html);
19969         }
19970         case 'paragraph': {
19971           return this.renderer.paragraph(this.inline.output(this.token.text));
19972         }
19973         case 'text': {
19974           return this.renderer.paragraph(this.parseText());
19975         }
19976       }
19977     };
19978   
19979     
19980     /**
19981      * Marked
19982      */
19983          /**
19984          * eval:var:marked
19985     */
19986     var marked = function (src, opt, callback) {
19987       if (callback || typeof opt === 'function') {
19988         if (!callback) {
19989           callback = opt;
19990           opt = null;
19991         }
19992     
19993         opt = merge({}, marked.defaults, opt || {});
19994     
19995         var highlight = opt.highlight
19996           , tokens
19997           , pending
19998           , i = 0;
19999     
20000         try {
20001           tokens = Lexer.lex(src, opt)
20002         } catch (e) {
20003           return callback(e);
20004         }
20005     
20006         pending = tokens.length;
20007          /**
20008          * eval:var:done
20009     */
20010         var done = function(err) {
20011           if (err) {
20012             opt.highlight = highlight;
20013             return callback(err);
20014           }
20015     
20016           var out;
20017     
20018           try {
20019             out = Parser.parse(tokens, opt);
20020           } catch (e) {
20021             err = e;
20022           }
20023     
20024           opt.highlight = highlight;
20025     
20026           return err
20027             ? callback(err)
20028             : callback(null, out);
20029         };
20030     
20031         if (!highlight || highlight.length < 3) {
20032           return done();
20033         }
20034     
20035         delete opt.highlight;
20036     
20037         if (!pending) { return done(); }
20038     
20039         for (; i < tokens.length; i++) {
20040           (function(token) {
20041             if (token.type !== 'code') {
20042               return --pending || done();
20043             }
20044             return highlight(token.text, token.lang, function(err, code) {
20045               if (err) { return done(err); }
20046               if (code == null || code === token.text) {
20047                 return --pending || done();
20048               }
20049               token.text = code;
20050               token.escaped = true;
20051               --pending || done();
20052             });
20053           })(tokens[i]);
20054         }
20055     
20056         return;
20057       }
20058       try {
20059         if (opt) { opt = merge({}, marked.defaults, opt); }
20060         return Parser.parse(Lexer.lex(src, opt), opt);
20061       } catch (e) {
20062         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20063         if ((opt || marked.defaults).silent) {
20064           return '<p>An error occured:</p><pre>'
20065             + escape(e.message + '', true)
20066             + '</pre>';
20067         }
20068         throw e;
20069       }
20070     }
20071     
20072     /**
20073      * Options
20074      */
20075     
20076     marked.options =
20077     marked.setOptions = function(opt) {
20078       merge(marked.defaults, opt);
20079       return marked;
20080     };
20081     
20082     marked.defaults = {
20083       gfm: true,
20084       tables: true,
20085       breaks: false,
20086       pedantic: false,
20087       sanitize: false,
20088       sanitizer: null,
20089       mangle: true,
20090       smartLists: false,
20091       silent: false,
20092       highlight: null,
20093       langPrefix: 'lang-',
20094       smartypants: false,
20095       headerPrefix: '',
20096       renderer: new Renderer,
20097       xhtml: false
20098     };
20099     
20100     /**
20101      * Expose
20102      */
20103     
20104     marked.Parser = Parser;
20105     marked.parser = Parser.parse;
20106     
20107     marked.Renderer = Renderer;
20108     
20109     marked.Lexer = Lexer;
20110     marked.lexer = Lexer.lex;
20111     
20112     marked.InlineLexer = InlineLexer;
20113     marked.inlineLexer = InlineLexer.output;
20114     
20115     marked.parse = marked;
20116     
20117     Roo.Markdown.marked = marked;
20118
20119 })();/*
20120  * Based on:
20121  * Ext JS Library 1.1.1
20122  * Copyright(c) 2006-2007, Ext JS, LLC.
20123  *
20124  * Originally Released Under LGPL - original licence link has changed is not relivant.
20125  *
20126  * Fork - LGPL
20127  * <script type="text/javascript">
20128  */
20129
20130
20131
20132 /*
20133  * These classes are derivatives of the similarly named classes in the YUI Library.
20134  * The original license:
20135  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20136  * Code licensed under the BSD License:
20137  * http://developer.yahoo.net/yui/license.txt
20138  */
20139
20140 (function() {
20141
20142 var Event=Roo.EventManager;
20143 var Dom=Roo.lib.Dom;
20144
20145 /**
20146  * @class Roo.dd.DragDrop
20147  * @extends Roo.util.Observable
20148  * Defines the interface and base operation of items that that can be
20149  * dragged or can be drop targets.  It was designed to be extended, overriding
20150  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20151  * Up to three html elements can be associated with a DragDrop instance:
20152  * <ul>
20153  * <li>linked element: the element that is passed into the constructor.
20154  * This is the element which defines the boundaries for interaction with
20155  * other DragDrop objects.</li>
20156  * <li>handle element(s): The drag operation only occurs if the element that
20157  * was clicked matches a handle element.  By default this is the linked
20158  * element, but there are times that you will want only a portion of the
20159  * linked element to initiate the drag operation, and the setHandleElId()
20160  * method provides a way to define this.</li>
20161  * <li>drag element: this represents the element that would be moved along
20162  * with the cursor during a drag operation.  By default, this is the linked
20163  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20164  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20165  * </li>
20166  * </ul>
20167  * This class should not be instantiated until the onload event to ensure that
20168  * the associated elements are available.
20169  * The following would define a DragDrop obj that would interact with any
20170  * other DragDrop obj in the "group1" group:
20171  * <pre>
20172  *  dd = new Roo.dd.DragDrop("div1", "group1");
20173  * </pre>
20174  * Since none of the event handlers have been implemented, nothing would
20175  * actually happen if you were to run the code above.  Normally you would
20176  * override this class or one of the default implementations, but you can
20177  * also override the methods you want on an instance of the class...
20178  * <pre>
20179  *  dd.onDragDrop = function(e, id) {
20180  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20181  *  }
20182  * </pre>
20183  * @constructor
20184  * @param {String} id of the element that is linked to this instance
20185  * @param {String} sGroup the group of related DragDrop objects
20186  * @param {object} config an object containing configurable attributes
20187  *                Valid properties for DragDrop:
20188  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20189  */
20190 Roo.dd.DragDrop = function(id, sGroup, config) {
20191     if (id) {
20192         this.init(id, sGroup, config);
20193     }
20194     
20195 };
20196
20197 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20198
20199     /**
20200      * The id of the element associated with this object.  This is what we
20201      * refer to as the "linked element" because the size and position of
20202      * this element is used to determine when the drag and drop objects have
20203      * interacted.
20204      * @property id
20205      * @type String
20206      */
20207     id: null,
20208
20209     /**
20210      * Configuration attributes passed into the constructor
20211      * @property config
20212      * @type object
20213      */
20214     config: null,
20215
20216     /**
20217      * The id of the element that will be dragged.  By default this is same
20218      * as the linked element , but could be changed to another element. Ex:
20219      * Roo.dd.DDProxy
20220      * @property dragElId
20221      * @type String
20222      * @private
20223      */
20224     dragElId: null,
20225
20226     /**
20227      * the id of the element that initiates the drag operation.  By default
20228      * this is the linked element, but could be changed to be a child of this
20229      * element.  This lets us do things like only starting the drag when the
20230      * header element within the linked html element is clicked.
20231      * @property handleElId
20232      * @type String
20233      * @private
20234      */
20235     handleElId: null,
20236
20237     /**
20238      * An associative array of HTML tags that will be ignored if clicked.
20239      * @property invalidHandleTypes
20240      * @type {string: string}
20241      */
20242     invalidHandleTypes: null,
20243
20244     /**
20245      * An associative array of ids for elements that will be ignored if clicked
20246      * @property invalidHandleIds
20247      * @type {string: string}
20248      */
20249     invalidHandleIds: null,
20250
20251     /**
20252      * An indexted array of css class names for elements that will be ignored
20253      * if clicked.
20254      * @property invalidHandleClasses
20255      * @type string[]
20256      */
20257     invalidHandleClasses: null,
20258
20259     /**
20260      * The linked element's absolute X position at the time the drag was
20261      * started
20262      * @property startPageX
20263      * @type int
20264      * @private
20265      */
20266     startPageX: 0,
20267
20268     /**
20269      * The linked element's absolute X position at the time the drag was
20270      * started
20271      * @property startPageY
20272      * @type int
20273      * @private
20274      */
20275     startPageY: 0,
20276
20277     /**
20278      * The group defines a logical collection of DragDrop objects that are
20279      * related.  Instances only get events when interacting with other
20280      * DragDrop object in the same group.  This lets us define multiple
20281      * groups using a single DragDrop subclass if we want.
20282      * @property groups
20283      * @type {string: string}
20284      */
20285     groups: null,
20286
20287     /**
20288      * Individual drag/drop instances can be locked.  This will prevent
20289      * onmousedown start drag.
20290      * @property locked
20291      * @type boolean
20292      * @private
20293      */
20294     locked: false,
20295
20296     /**
20297      * Lock this instance
20298      * @method lock
20299      */
20300     lock: function() { this.locked = true; },
20301
20302     /**
20303      * Unlock this instace
20304      * @method unlock
20305      */
20306     unlock: function() { this.locked = false; },
20307
20308     /**
20309      * By default, all insances can be a drop target.  This can be disabled by
20310      * setting isTarget to false.
20311      * @method isTarget
20312      * @type boolean
20313      */
20314     isTarget: true,
20315
20316     /**
20317      * The padding configured for this drag and drop object for calculating
20318      * the drop zone intersection with this object.
20319      * @method padding
20320      * @type int[]
20321      */
20322     padding: null,
20323
20324     /**
20325      * Cached reference to the linked element
20326      * @property _domRef
20327      * @private
20328      */
20329     _domRef: null,
20330
20331     /**
20332      * Internal typeof flag
20333      * @property __ygDragDrop
20334      * @private
20335      */
20336     __ygDragDrop: true,
20337
20338     /**
20339      * Set to true when horizontal contraints are applied
20340      * @property constrainX
20341      * @type boolean
20342      * @private
20343      */
20344     constrainX: false,
20345
20346     /**
20347      * Set to true when vertical contraints are applied
20348      * @property constrainY
20349      * @type boolean
20350      * @private
20351      */
20352     constrainY: false,
20353
20354     /**
20355      * The left constraint
20356      * @property minX
20357      * @type int
20358      * @private
20359      */
20360     minX: 0,
20361
20362     /**
20363      * The right constraint
20364      * @property maxX
20365      * @type int
20366      * @private
20367      */
20368     maxX: 0,
20369
20370     /**
20371      * The up constraint
20372      * @property minY
20373      * @type int
20374      * @type int
20375      * @private
20376      */
20377     minY: 0,
20378
20379     /**
20380      * The down constraint
20381      * @property maxY
20382      * @type int
20383      * @private
20384      */
20385     maxY: 0,
20386
20387     /**
20388      * Maintain offsets when we resetconstraints.  Set to true when you want
20389      * the position of the element relative to its parent to stay the same
20390      * when the page changes
20391      *
20392      * @property maintainOffset
20393      * @type boolean
20394      */
20395     maintainOffset: false,
20396
20397     /**
20398      * Array of pixel locations the element will snap to if we specified a
20399      * horizontal graduation/interval.  This array is generated automatically
20400      * when you define a tick interval.
20401      * @property xTicks
20402      * @type int[]
20403      */
20404     xTicks: null,
20405
20406     /**
20407      * Array of pixel locations the element will snap to if we specified a
20408      * vertical graduation/interval.  This array is generated automatically
20409      * when you define a tick interval.
20410      * @property yTicks
20411      * @type int[]
20412      */
20413     yTicks: null,
20414
20415     /**
20416      * By default the drag and drop instance will only respond to the primary
20417      * button click (left button for a right-handed mouse).  Set to true to
20418      * allow drag and drop to start with any mouse click that is propogated
20419      * by the browser
20420      * @property primaryButtonOnly
20421      * @type boolean
20422      */
20423     primaryButtonOnly: true,
20424
20425     /**
20426      * The availabe property is false until the linked dom element is accessible.
20427      * @property available
20428      * @type boolean
20429      */
20430     available: false,
20431
20432     /**
20433      * By default, drags can only be initiated if the mousedown occurs in the
20434      * region the linked element is.  This is done in part to work around a
20435      * bug in some browsers that mis-report the mousedown if the previous
20436      * mouseup happened outside of the window.  This property is set to true
20437      * if outer handles are defined.
20438      *
20439      * @property hasOuterHandles
20440      * @type boolean
20441      * @default false
20442      */
20443     hasOuterHandles: false,
20444
20445     /**
20446      * Code that executes immediately before the startDrag event
20447      * @method b4StartDrag
20448      * @private
20449      */
20450     b4StartDrag: function(x, y) { },
20451
20452     /**
20453      * Abstract method called after a drag/drop object is clicked
20454      * and the drag or mousedown time thresholds have beeen met.
20455      * @method startDrag
20456      * @param {int} X click location
20457      * @param {int} Y click location
20458      */
20459     startDrag: function(x, y) { /* override this */ },
20460
20461     /**
20462      * Code that executes immediately before the onDrag event
20463      * @method b4Drag
20464      * @private
20465      */
20466     b4Drag: function(e) { },
20467
20468     /**
20469      * Abstract method called during the onMouseMove event while dragging an
20470      * object.
20471      * @method onDrag
20472      * @param {Event} e the mousemove event
20473      */
20474     onDrag: function(e) { /* override this */ },
20475
20476     /**
20477      * Abstract method called when this element fist begins hovering over
20478      * another DragDrop obj
20479      * @method onDragEnter
20480      * @param {Event} e the mousemove event
20481      * @param {String|DragDrop[]} id In POINT mode, the element
20482      * id this is hovering over.  In INTERSECT mode, an array of one or more
20483      * dragdrop items being hovered over.
20484      */
20485     onDragEnter: function(e, id) { /* override this */ },
20486
20487     /**
20488      * Code that executes immediately before the onDragOver event
20489      * @method b4DragOver
20490      * @private
20491      */
20492     b4DragOver: function(e) { },
20493
20494     /**
20495      * Abstract method called when this element is hovering over another
20496      * DragDrop obj
20497      * @method onDragOver
20498      * @param {Event} e the mousemove event
20499      * @param {String|DragDrop[]} id In POINT mode, the element
20500      * id this is hovering over.  In INTERSECT mode, an array of dd items
20501      * being hovered over.
20502      */
20503     onDragOver: function(e, id) { /* override this */ },
20504
20505     /**
20506      * Code that executes immediately before the onDragOut event
20507      * @method b4DragOut
20508      * @private
20509      */
20510     b4DragOut: function(e) { },
20511
20512     /**
20513      * Abstract method called when we are no longer hovering over an element
20514      * @method onDragOut
20515      * @param {Event} e the mousemove event
20516      * @param {String|DragDrop[]} id In POINT mode, the element
20517      * id this was hovering over.  In INTERSECT mode, an array of dd items
20518      * that the mouse is no longer over.
20519      */
20520     onDragOut: function(e, id) { /* override this */ },
20521
20522     /**
20523      * Code that executes immediately before the onDragDrop event
20524      * @method b4DragDrop
20525      * @private
20526      */
20527     b4DragDrop: function(e) { },
20528
20529     /**
20530      * Abstract method called when this item is dropped on another DragDrop
20531      * obj
20532      * @method onDragDrop
20533      * @param {Event} e the mouseup event
20534      * @param {String|DragDrop[]} id In POINT mode, the element
20535      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20536      * was dropped on.
20537      */
20538     onDragDrop: function(e, id) { /* override this */ },
20539
20540     /**
20541      * Abstract method called when this item is dropped on an area with no
20542      * drop target
20543      * @method onInvalidDrop
20544      * @param {Event} e the mouseup event
20545      */
20546     onInvalidDrop: function(e) { /* override this */ },
20547
20548     /**
20549      * Code that executes immediately before the endDrag event
20550      * @method b4EndDrag
20551      * @private
20552      */
20553     b4EndDrag: function(e) { },
20554
20555     /**
20556      * Fired when we are done dragging the object
20557      * @method endDrag
20558      * @param {Event} e the mouseup event
20559      */
20560     endDrag: function(e) { /* override this */ },
20561
20562     /**
20563      * Code executed immediately before the onMouseDown event
20564      * @method b4MouseDown
20565      * @param {Event} e the mousedown event
20566      * @private
20567      */
20568     b4MouseDown: function(e) {  },
20569
20570     /**
20571      * Event handler that fires when a drag/drop obj gets a mousedown
20572      * @method onMouseDown
20573      * @param {Event} e the mousedown event
20574      */
20575     onMouseDown: function(e) { /* override this */ },
20576
20577     /**
20578      * Event handler that fires when a drag/drop obj gets a mouseup
20579      * @method onMouseUp
20580      * @param {Event} e the mouseup event
20581      */
20582     onMouseUp: function(e) { /* override this */ },
20583
20584     /**
20585      * Override the onAvailable method to do what is needed after the initial
20586      * position was determined.
20587      * @method onAvailable
20588      */
20589     onAvailable: function () {
20590     },
20591
20592     /*
20593      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20594      * @type Object
20595      */
20596     defaultPadding : {left:0, right:0, top:0, bottom:0},
20597
20598     /*
20599      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20600  *
20601  * Usage:
20602  <pre><code>
20603  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20604                 { dragElId: "existingProxyDiv" });
20605  dd.startDrag = function(){
20606      this.constrainTo("parent-id");
20607  };
20608  </code></pre>
20609  * Or you can initalize it using the {@link Roo.Element} object:
20610  <pre><code>
20611  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20612      startDrag : function(){
20613          this.constrainTo("parent-id");
20614      }
20615  });
20616  </code></pre>
20617      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20618      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20619      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20620      * an object containing the sides to pad. For example: {right:10, bottom:10}
20621      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20622      */
20623     constrainTo : function(constrainTo, pad, inContent){
20624         if(typeof pad == "number"){
20625             pad = {left: pad, right:pad, top:pad, bottom:pad};
20626         }
20627         pad = pad || this.defaultPadding;
20628         var b = Roo.get(this.getEl()).getBox();
20629         var ce = Roo.get(constrainTo);
20630         var s = ce.getScroll();
20631         var c, cd = ce.dom;
20632         if(cd == document.body){
20633             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20634         }else{
20635             xy = ce.getXY();
20636             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20637         }
20638
20639
20640         var topSpace = b.y - c.y;
20641         var leftSpace = b.x - c.x;
20642
20643         this.resetConstraints();
20644         this.setXConstraint(leftSpace - (pad.left||0), // left
20645                 c.width - leftSpace - b.width - (pad.right||0) //right
20646         );
20647         this.setYConstraint(topSpace - (pad.top||0), //top
20648                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20649         );
20650     },
20651
20652     /**
20653      * Returns a reference to the linked element
20654      * @method getEl
20655      * @return {HTMLElement} the html element
20656      */
20657     getEl: function() {
20658         if (!this._domRef) {
20659             this._domRef = Roo.getDom(this.id);
20660         }
20661
20662         return this._domRef;
20663     },
20664
20665     /**
20666      * Returns a reference to the actual element to drag.  By default this is
20667      * the same as the html element, but it can be assigned to another
20668      * element. An example of this can be found in Roo.dd.DDProxy
20669      * @method getDragEl
20670      * @return {HTMLElement} the html element
20671      */
20672     getDragEl: function() {
20673         return Roo.getDom(this.dragElId);
20674     },
20675
20676     /**
20677      * Sets up the DragDrop object.  Must be called in the constructor of any
20678      * Roo.dd.DragDrop subclass
20679      * @method init
20680      * @param id the id of the linked element
20681      * @param {String} sGroup the group of related items
20682      * @param {object} config configuration attributes
20683      */
20684     init: function(id, sGroup, config) {
20685         this.initTarget(id, sGroup, config);
20686         if (!Roo.isTouch) {
20687             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20688         }
20689         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20690         // Event.on(this.id, "selectstart", Event.preventDefault);
20691     },
20692
20693     /**
20694      * Initializes Targeting functionality only... the object does not
20695      * get a mousedown handler.
20696      * @method initTarget
20697      * @param id the id of the linked element
20698      * @param {String} sGroup the group of related items
20699      * @param {object} config configuration attributes
20700      */
20701     initTarget: function(id, sGroup, config) {
20702
20703         // configuration attributes
20704         this.config = config || {};
20705
20706         // create a local reference to the drag and drop manager
20707         this.DDM = Roo.dd.DDM;
20708         // initialize the groups array
20709         this.groups = {};
20710
20711         // assume that we have an element reference instead of an id if the
20712         // parameter is not a string
20713         if (typeof id !== "string") {
20714             id = Roo.id(id);
20715         }
20716
20717         // set the id
20718         this.id = id;
20719
20720         // add to an interaction group
20721         this.addToGroup((sGroup) ? sGroup : "default");
20722
20723         // We don't want to register this as the handle with the manager
20724         // so we just set the id rather than calling the setter.
20725         this.handleElId = id;
20726
20727         // the linked element is the element that gets dragged by default
20728         this.setDragElId(id);
20729
20730         // by default, clicked anchors will not start drag operations.
20731         this.invalidHandleTypes = { A: "A" };
20732         this.invalidHandleIds = {};
20733         this.invalidHandleClasses = [];
20734
20735         this.applyConfig();
20736
20737         this.handleOnAvailable();
20738     },
20739
20740     /**
20741      * Applies the configuration parameters that were passed into the constructor.
20742      * This is supposed to happen at each level through the inheritance chain.  So
20743      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20744      * DragDrop in order to get all of the parameters that are available in
20745      * each object.
20746      * @method applyConfig
20747      */
20748     applyConfig: function() {
20749
20750         // configurable properties:
20751         //    padding, isTarget, maintainOffset, primaryButtonOnly
20752         this.padding           = this.config.padding || [0, 0, 0, 0];
20753         this.isTarget          = (this.config.isTarget !== false);
20754         this.maintainOffset    = (this.config.maintainOffset);
20755         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20756
20757     },
20758
20759     /**
20760      * Executed when the linked element is available
20761      * @method handleOnAvailable
20762      * @private
20763      */
20764     handleOnAvailable: function() {
20765         this.available = true;
20766         this.resetConstraints();
20767         this.onAvailable();
20768     },
20769
20770      /**
20771      * Configures the padding for the target zone in px.  Effectively expands
20772      * (or reduces) the virtual object size for targeting calculations.
20773      * Supports css-style shorthand; if only one parameter is passed, all sides
20774      * will have that padding, and if only two are passed, the top and bottom
20775      * will have the first param, the left and right the second.
20776      * @method setPadding
20777      * @param {int} iTop    Top pad
20778      * @param {int} iRight  Right pad
20779      * @param {int} iBot    Bot pad
20780      * @param {int} iLeft   Left pad
20781      */
20782     setPadding: function(iTop, iRight, iBot, iLeft) {
20783         // this.padding = [iLeft, iRight, iTop, iBot];
20784         if (!iRight && 0 !== iRight) {
20785             this.padding = [iTop, iTop, iTop, iTop];
20786         } else if (!iBot && 0 !== iBot) {
20787             this.padding = [iTop, iRight, iTop, iRight];
20788         } else {
20789             this.padding = [iTop, iRight, iBot, iLeft];
20790         }
20791     },
20792
20793     /**
20794      * Stores the initial placement of the linked element.
20795      * @method setInitialPosition
20796      * @param {int} diffX   the X offset, default 0
20797      * @param {int} diffY   the Y offset, default 0
20798      */
20799     setInitPosition: function(diffX, diffY) {
20800         var el = this.getEl();
20801
20802         if (!this.DDM.verifyEl(el)) {
20803             return;
20804         }
20805
20806         var dx = diffX || 0;
20807         var dy = diffY || 0;
20808
20809         var p = Dom.getXY( el );
20810
20811         this.initPageX = p[0] - dx;
20812         this.initPageY = p[1] - dy;
20813
20814         this.lastPageX = p[0];
20815         this.lastPageY = p[1];
20816
20817
20818         this.setStartPosition(p);
20819     },
20820
20821     /**
20822      * Sets the start position of the element.  This is set when the obj
20823      * is initialized, the reset when a drag is started.
20824      * @method setStartPosition
20825      * @param pos current position (from previous lookup)
20826      * @private
20827      */
20828     setStartPosition: function(pos) {
20829         var p = pos || Dom.getXY( this.getEl() );
20830         this.deltaSetXY = null;
20831
20832         this.startPageX = p[0];
20833         this.startPageY = p[1];
20834     },
20835
20836     /**
20837      * Add this instance to a group of related drag/drop objects.  All
20838      * instances belong to at least one group, and can belong to as many
20839      * groups as needed.
20840      * @method addToGroup
20841      * @param sGroup {string} the name of the group
20842      */
20843     addToGroup: function(sGroup) {
20844         this.groups[sGroup] = true;
20845         this.DDM.regDragDrop(this, sGroup);
20846     },
20847
20848     /**
20849      * Remove's this instance from the supplied interaction group
20850      * @method removeFromGroup
20851      * @param {string}  sGroup  The group to drop
20852      */
20853     removeFromGroup: function(sGroup) {
20854         if (this.groups[sGroup]) {
20855             delete this.groups[sGroup];
20856         }
20857
20858         this.DDM.removeDDFromGroup(this, sGroup);
20859     },
20860
20861     /**
20862      * Allows you to specify that an element other than the linked element
20863      * will be moved with the cursor during a drag
20864      * @method setDragElId
20865      * @param id {string} the id of the element that will be used to initiate the drag
20866      */
20867     setDragElId: function(id) {
20868         this.dragElId = id;
20869     },
20870
20871     /**
20872      * Allows you to specify a child of the linked element that should be
20873      * used to initiate the drag operation.  An example of this would be if
20874      * you have a content div with text and links.  Clicking anywhere in the
20875      * content area would normally start the drag operation.  Use this method
20876      * to specify that an element inside of the content div is the element
20877      * that starts the drag operation.
20878      * @method setHandleElId
20879      * @param id {string} the id of the element that will be used to
20880      * initiate the drag.
20881      */
20882     setHandleElId: function(id) {
20883         if (typeof id !== "string") {
20884             id = Roo.id(id);
20885         }
20886         this.handleElId = id;
20887         this.DDM.regHandle(this.id, id);
20888     },
20889
20890     /**
20891      * Allows you to set an element outside of the linked element as a drag
20892      * handle
20893      * @method setOuterHandleElId
20894      * @param id the id of the element that will be used to initiate the drag
20895      */
20896     setOuterHandleElId: function(id) {
20897         if (typeof id !== "string") {
20898             id = Roo.id(id);
20899         }
20900         Event.on(id, "mousedown",
20901                 this.handleMouseDown, this);
20902         this.setHandleElId(id);
20903
20904         this.hasOuterHandles = true;
20905     },
20906
20907     /**
20908      * Remove all drag and drop hooks for this element
20909      * @method unreg
20910      */
20911     unreg: function() {
20912         Event.un(this.id, "mousedown",
20913                 this.handleMouseDown);
20914         Event.un(this.id, "touchstart",
20915                 this.handleMouseDown);
20916         this._domRef = null;
20917         this.DDM._remove(this);
20918     },
20919
20920     destroy : function(){
20921         this.unreg();
20922     },
20923
20924     /**
20925      * Returns true if this instance is locked, or the drag drop mgr is locked
20926      * (meaning that all drag/drop is disabled on the page.)
20927      * @method isLocked
20928      * @return {boolean} true if this obj or all drag/drop is locked, else
20929      * false
20930      */
20931     isLocked: function() {
20932         return (this.DDM.isLocked() || this.locked);
20933     },
20934
20935     /**
20936      * Fired when this object is clicked
20937      * @method handleMouseDown
20938      * @param {Event} e
20939      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20940      * @private
20941      */
20942     handleMouseDown: function(e, oDD){
20943      
20944         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20945             //Roo.log('not touch/ button !=0');
20946             return;
20947         }
20948         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20949             return; // double touch..
20950         }
20951         
20952
20953         if (this.isLocked()) {
20954             //Roo.log('locked');
20955             return;
20956         }
20957
20958         this.DDM.refreshCache(this.groups);
20959 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20960         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20961         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20962             //Roo.log('no outer handes or not over target');
20963                 // do nothing.
20964         } else {
20965 //            Roo.log('check validator');
20966             if (this.clickValidator(e)) {
20967 //                Roo.log('validate success');
20968                 // set the initial element position
20969                 this.setStartPosition();
20970
20971
20972                 this.b4MouseDown(e);
20973                 this.onMouseDown(e);
20974
20975                 this.DDM.handleMouseDown(e, this);
20976
20977                 this.DDM.stopEvent(e);
20978             } else {
20979
20980
20981             }
20982         }
20983     },
20984
20985     clickValidator: function(e) {
20986         var target = e.getTarget();
20987         return ( this.isValidHandleChild(target) &&
20988                     (this.id == this.handleElId ||
20989                         this.DDM.handleWasClicked(target, this.id)) );
20990     },
20991
20992     /**
20993      * Allows you to specify a tag name that should not start a drag operation
20994      * when clicked.  This is designed to facilitate embedding links within a
20995      * drag handle that do something other than start the drag.
20996      * @method addInvalidHandleType
20997      * @param {string} tagName the type of element to exclude
20998      */
20999     addInvalidHandleType: function(tagName) {
21000         var type = tagName.toUpperCase();
21001         this.invalidHandleTypes[type] = type;
21002     },
21003
21004     /**
21005      * Lets you to specify an element id for a child of a drag handle
21006      * that should not initiate a drag
21007      * @method addInvalidHandleId
21008      * @param {string} id the element id of the element you wish to ignore
21009      */
21010     addInvalidHandleId: function(id) {
21011         if (typeof id !== "string") {
21012             id = Roo.id(id);
21013         }
21014         this.invalidHandleIds[id] = id;
21015     },
21016
21017     /**
21018      * Lets you specify a css class of elements that will not initiate a drag
21019      * @method addInvalidHandleClass
21020      * @param {string} cssClass the class of the elements you wish to ignore
21021      */
21022     addInvalidHandleClass: function(cssClass) {
21023         this.invalidHandleClasses.push(cssClass);
21024     },
21025
21026     /**
21027      * Unsets an excluded tag name set by addInvalidHandleType
21028      * @method removeInvalidHandleType
21029      * @param {string} tagName the type of element to unexclude
21030      */
21031     removeInvalidHandleType: function(tagName) {
21032         var type = tagName.toUpperCase();
21033         // this.invalidHandleTypes[type] = null;
21034         delete this.invalidHandleTypes[type];
21035     },
21036
21037     /**
21038      * Unsets an invalid handle id
21039      * @method removeInvalidHandleId
21040      * @param {string} id the id of the element to re-enable
21041      */
21042     removeInvalidHandleId: function(id) {
21043         if (typeof id !== "string") {
21044             id = Roo.id(id);
21045         }
21046         delete this.invalidHandleIds[id];
21047     },
21048
21049     /**
21050      * Unsets an invalid css class
21051      * @method removeInvalidHandleClass
21052      * @param {string} cssClass the class of the element(s) you wish to
21053      * re-enable
21054      */
21055     removeInvalidHandleClass: function(cssClass) {
21056         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21057             if (this.invalidHandleClasses[i] == cssClass) {
21058                 delete this.invalidHandleClasses[i];
21059             }
21060         }
21061     },
21062
21063     /**
21064      * Checks the tag exclusion list to see if this click should be ignored
21065      * @method isValidHandleChild
21066      * @param {HTMLElement} node the HTMLElement to evaluate
21067      * @return {boolean} true if this is a valid tag type, false if not
21068      */
21069     isValidHandleChild: function(node) {
21070
21071         var valid = true;
21072         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21073         var nodeName;
21074         try {
21075             nodeName = node.nodeName.toUpperCase();
21076         } catch(e) {
21077             nodeName = node.nodeName;
21078         }
21079         valid = valid && !this.invalidHandleTypes[nodeName];
21080         valid = valid && !this.invalidHandleIds[node.id];
21081
21082         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21083             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21084         }
21085
21086
21087         return valid;
21088
21089     },
21090
21091     /**
21092      * Create the array of horizontal tick marks if an interval was specified
21093      * in setXConstraint().
21094      * @method setXTicks
21095      * @private
21096      */
21097     setXTicks: function(iStartX, iTickSize) {
21098         this.xTicks = [];
21099         this.xTickSize = iTickSize;
21100
21101         var tickMap = {};
21102
21103         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21104             if (!tickMap[i]) {
21105                 this.xTicks[this.xTicks.length] = i;
21106                 tickMap[i] = true;
21107             }
21108         }
21109
21110         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21111             if (!tickMap[i]) {
21112                 this.xTicks[this.xTicks.length] = i;
21113                 tickMap[i] = true;
21114             }
21115         }
21116
21117         this.xTicks.sort(this.DDM.numericSort) ;
21118     },
21119
21120     /**
21121      * Create the array of vertical tick marks if an interval was specified in
21122      * setYConstraint().
21123      * @method setYTicks
21124      * @private
21125      */
21126     setYTicks: function(iStartY, iTickSize) {
21127         this.yTicks = [];
21128         this.yTickSize = iTickSize;
21129
21130         var tickMap = {};
21131
21132         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21133             if (!tickMap[i]) {
21134                 this.yTicks[this.yTicks.length] = i;
21135                 tickMap[i] = true;
21136             }
21137         }
21138
21139         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21140             if (!tickMap[i]) {
21141                 this.yTicks[this.yTicks.length] = i;
21142                 tickMap[i] = true;
21143             }
21144         }
21145
21146         this.yTicks.sort(this.DDM.numericSort) ;
21147     },
21148
21149     /**
21150      * By default, the element can be dragged any place on the screen.  Use
21151      * this method to limit the horizontal travel of the element.  Pass in
21152      * 0,0 for the parameters if you want to lock the drag to the y axis.
21153      * @method setXConstraint
21154      * @param {int} iLeft the number of pixels the element can move to the left
21155      * @param {int} iRight the number of pixels the element can move to the
21156      * right
21157      * @param {int} iTickSize optional parameter for specifying that the
21158      * element
21159      * should move iTickSize pixels at a time.
21160      */
21161     setXConstraint: function(iLeft, iRight, iTickSize) {
21162         this.leftConstraint = iLeft;
21163         this.rightConstraint = iRight;
21164
21165         this.minX = this.initPageX - iLeft;
21166         this.maxX = this.initPageX + iRight;
21167         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21168
21169         this.constrainX = true;
21170     },
21171
21172     /**
21173      * Clears any constraints applied to this instance.  Also clears ticks
21174      * since they can't exist independent of a constraint at this time.
21175      * @method clearConstraints
21176      */
21177     clearConstraints: function() {
21178         this.constrainX = false;
21179         this.constrainY = false;
21180         this.clearTicks();
21181     },
21182
21183     /**
21184      * Clears any tick interval defined for this instance
21185      * @method clearTicks
21186      */
21187     clearTicks: function() {
21188         this.xTicks = null;
21189         this.yTicks = null;
21190         this.xTickSize = 0;
21191         this.yTickSize = 0;
21192     },
21193
21194     /**
21195      * By default, the element can be dragged any place on the screen.  Set
21196      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21197      * parameters if you want to lock the drag to the x axis.
21198      * @method setYConstraint
21199      * @param {int} iUp the number of pixels the element can move up
21200      * @param {int} iDown the number of pixels the element can move down
21201      * @param {int} iTickSize optional parameter for specifying that the
21202      * element should move iTickSize pixels at a time.
21203      */
21204     setYConstraint: function(iUp, iDown, iTickSize) {
21205         this.topConstraint = iUp;
21206         this.bottomConstraint = iDown;
21207
21208         this.minY = this.initPageY - iUp;
21209         this.maxY = this.initPageY + iDown;
21210         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21211
21212         this.constrainY = true;
21213
21214     },
21215
21216     /**
21217      * resetConstraints must be called if you manually reposition a dd element.
21218      * @method resetConstraints
21219      * @param {boolean} maintainOffset
21220      */
21221     resetConstraints: function() {
21222
21223
21224         // Maintain offsets if necessary
21225         if (this.initPageX || this.initPageX === 0) {
21226             // figure out how much this thing has moved
21227             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21228             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21229
21230             this.setInitPosition(dx, dy);
21231
21232         // This is the first time we have detected the element's position
21233         } else {
21234             this.setInitPosition();
21235         }
21236
21237         if (this.constrainX) {
21238             this.setXConstraint( this.leftConstraint,
21239                                  this.rightConstraint,
21240                                  this.xTickSize        );
21241         }
21242
21243         if (this.constrainY) {
21244             this.setYConstraint( this.topConstraint,
21245                                  this.bottomConstraint,
21246                                  this.yTickSize         );
21247         }
21248     },
21249
21250     /**
21251      * Normally the drag element is moved pixel by pixel, but we can specify
21252      * that it move a number of pixels at a time.  This method resolves the
21253      * location when we have it set up like this.
21254      * @method getTick
21255      * @param {int} val where we want to place the object
21256      * @param {int[]} tickArray sorted array of valid points
21257      * @return {int} the closest tick
21258      * @private
21259      */
21260     getTick: function(val, tickArray) {
21261
21262         if (!tickArray) {
21263             // If tick interval is not defined, it is effectively 1 pixel,
21264             // so we return the value passed to us.
21265             return val;
21266         } else if (tickArray[0] >= val) {
21267             // The value is lower than the first tick, so we return the first
21268             // tick.
21269             return tickArray[0];
21270         } else {
21271             for (var i=0, len=tickArray.length; i<len; ++i) {
21272                 var next = i + 1;
21273                 if (tickArray[next] && tickArray[next] >= val) {
21274                     var diff1 = val - tickArray[i];
21275                     var diff2 = tickArray[next] - val;
21276                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21277                 }
21278             }
21279
21280             // The value is larger than the last tick, so we return the last
21281             // tick.
21282             return tickArray[tickArray.length - 1];
21283         }
21284     },
21285
21286     /**
21287      * toString method
21288      * @method toString
21289      * @return {string} string representation of the dd obj
21290      */
21291     toString: function() {
21292         return ("DragDrop " + this.id);
21293     }
21294
21295 });
21296
21297 })();
21298 /*
21299  * Based on:
21300  * Ext JS Library 1.1.1
21301  * Copyright(c) 2006-2007, Ext JS, LLC.
21302  *
21303  * Originally Released Under LGPL - original licence link has changed is not relivant.
21304  *
21305  * Fork - LGPL
21306  * <script type="text/javascript">
21307  */
21308
21309
21310 /**
21311  * The drag and drop utility provides a framework for building drag and drop
21312  * applications.  In addition to enabling drag and drop for specific elements,
21313  * the drag and drop elements are tracked by the manager class, and the
21314  * interactions between the various elements are tracked during the drag and
21315  * the implementing code is notified about these important moments.
21316  */
21317
21318 // Only load the library once.  Rewriting the manager class would orphan
21319 // existing drag and drop instances.
21320 if (!Roo.dd.DragDropMgr) {
21321
21322 /**
21323  * @class Roo.dd.DragDropMgr
21324  * DragDropMgr is a singleton that tracks the element interaction for
21325  * all DragDrop items in the window.  Generally, you will not call
21326  * this class directly, but it does have helper methods that could
21327  * be useful in your DragDrop implementations.
21328  * @static
21329  */
21330 Roo.dd.DragDropMgr = function() {
21331
21332     var Event = Roo.EventManager;
21333
21334     return {
21335
21336         /**
21337          * Two dimensional Array of registered DragDrop objects.  The first
21338          * dimension is the DragDrop item group, the second the DragDrop
21339          * object.
21340          * @property ids
21341          * @type {string: string}
21342          * @private
21343          * @static
21344          */
21345         ids: {},
21346
21347         /**
21348          * Array of element ids defined as drag handles.  Used to determine
21349          * if the element that generated the mousedown event is actually the
21350          * handle and not the html element itself.
21351          * @property handleIds
21352          * @type {string: string}
21353          * @private
21354          * @static
21355          */
21356         handleIds: {},
21357
21358         /**
21359          * the DragDrop object that is currently being dragged
21360          * @property dragCurrent
21361          * @type DragDrop
21362          * @private
21363          * @static
21364          **/
21365         dragCurrent: null,
21366
21367         /**
21368          * the DragDrop object(s) that are being hovered over
21369          * @property dragOvers
21370          * @type Array
21371          * @private
21372          * @static
21373          */
21374         dragOvers: {},
21375
21376         /**
21377          * the X distance between the cursor and the object being dragged
21378          * @property deltaX
21379          * @type int
21380          * @private
21381          * @static
21382          */
21383         deltaX: 0,
21384
21385         /**
21386          * the Y distance between the cursor and the object being dragged
21387          * @property deltaY
21388          * @type int
21389          * @private
21390          * @static
21391          */
21392         deltaY: 0,
21393
21394         /**
21395          * Flag to determine if we should prevent the default behavior of the
21396          * events we define. By default this is true, but this can be set to
21397          * false if you need the default behavior (not recommended)
21398          * @property preventDefault
21399          * @type boolean
21400          * @static
21401          */
21402         preventDefault: true,
21403
21404         /**
21405          * Flag to determine if we should stop the propagation of the events
21406          * we generate. This is true by default but you may want to set it to
21407          * false if the html element contains other features that require the
21408          * mouse click.
21409          * @property stopPropagation
21410          * @type boolean
21411          * @static
21412          */
21413         stopPropagation: true,
21414
21415         /**
21416          * Internal flag that is set to true when drag and drop has been
21417          * intialized
21418          * @property initialized
21419          * @private
21420          * @static
21421          */
21422         initalized: false,
21423
21424         /**
21425          * All drag and drop can be disabled.
21426          * @property locked
21427          * @private
21428          * @static
21429          */
21430         locked: false,
21431
21432         /**
21433          * Called the first time an element is registered.
21434          * @method init
21435          * @private
21436          * @static
21437          */
21438         init: function() {
21439             this.initialized = true;
21440         },
21441
21442         /**
21443          * In point mode, drag and drop interaction is defined by the
21444          * location of the cursor during the drag/drop
21445          * @property POINT
21446          * @type int
21447          * @static
21448          */
21449         POINT: 0,
21450
21451         /**
21452          * In intersect mode, drag and drop interactio nis defined by the
21453          * overlap of two or more drag and drop objects.
21454          * @property INTERSECT
21455          * @type int
21456          * @static
21457          */
21458         INTERSECT: 1,
21459
21460         /**
21461          * The current drag and drop mode.  Default: POINT
21462          * @property mode
21463          * @type int
21464          * @static
21465          */
21466         mode: 0,
21467
21468         /**
21469          * Runs method on all drag and drop objects
21470          * @method _execOnAll
21471          * @private
21472          * @static
21473          */
21474         _execOnAll: function(sMethod, args) {
21475             for (var i in this.ids) {
21476                 for (var j in this.ids[i]) {
21477                     var oDD = this.ids[i][j];
21478                     if (! this.isTypeOfDD(oDD)) {
21479                         continue;
21480                     }
21481                     oDD[sMethod].apply(oDD, args);
21482                 }
21483             }
21484         },
21485
21486         /**
21487          * Drag and drop initialization.  Sets up the global event handlers
21488          * @method _onLoad
21489          * @private
21490          * @static
21491          */
21492         _onLoad: function() {
21493
21494             this.init();
21495
21496             if (!Roo.isTouch) {
21497                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21498                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21499             }
21500             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21501             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21502             
21503             Event.on(window,   "unload",    this._onUnload, this, true);
21504             Event.on(window,   "resize",    this._onResize, this, true);
21505             // Event.on(window,   "mouseout",    this._test);
21506
21507         },
21508
21509         /**
21510          * Reset constraints on all drag and drop objs
21511          * @method _onResize
21512          * @private
21513          * @static
21514          */
21515         _onResize: function(e) {
21516             this._execOnAll("resetConstraints", []);
21517         },
21518
21519         /**
21520          * Lock all drag and drop functionality
21521          * @method lock
21522          * @static
21523          */
21524         lock: function() { this.locked = true; },
21525
21526         /**
21527          * Unlock all drag and drop functionality
21528          * @method unlock
21529          * @static
21530          */
21531         unlock: function() { this.locked = false; },
21532
21533         /**
21534          * Is drag and drop locked?
21535          * @method isLocked
21536          * @return {boolean} True if drag and drop is locked, false otherwise.
21537          * @static
21538          */
21539         isLocked: function() { return this.locked; },
21540
21541         /**
21542          * Location cache that is set for all drag drop objects when a drag is
21543          * initiated, cleared when the drag is finished.
21544          * @property locationCache
21545          * @private
21546          * @static
21547          */
21548         locationCache: {},
21549
21550         /**
21551          * Set useCache to false if you want to force object the lookup of each
21552          * drag and drop linked element constantly during a drag.
21553          * @property useCache
21554          * @type boolean
21555          * @static
21556          */
21557         useCache: true,
21558
21559         /**
21560          * The number of pixels that the mouse needs to move after the
21561          * mousedown before the drag is initiated.  Default=3;
21562          * @property clickPixelThresh
21563          * @type int
21564          * @static
21565          */
21566         clickPixelThresh: 3,
21567
21568         /**
21569          * The number of milliseconds after the mousedown event to initiate the
21570          * drag if we don't get a mouseup event. Default=1000
21571          * @property clickTimeThresh
21572          * @type int
21573          * @static
21574          */
21575         clickTimeThresh: 350,
21576
21577         /**
21578          * Flag that indicates that either the drag pixel threshold or the
21579          * mousdown time threshold has been met
21580          * @property dragThreshMet
21581          * @type boolean
21582          * @private
21583          * @static
21584          */
21585         dragThreshMet: false,
21586
21587         /**
21588          * Timeout used for the click time threshold
21589          * @property clickTimeout
21590          * @type Object
21591          * @private
21592          * @static
21593          */
21594         clickTimeout: null,
21595
21596         /**
21597          * The X position of the mousedown event stored for later use when a
21598          * drag threshold is met.
21599          * @property startX
21600          * @type int
21601          * @private
21602          * @static
21603          */
21604         startX: 0,
21605
21606         /**
21607          * The Y position of the mousedown event stored for later use when a
21608          * drag threshold is met.
21609          * @property startY
21610          * @type int
21611          * @private
21612          * @static
21613          */
21614         startY: 0,
21615
21616         /**
21617          * Each DragDrop instance must be registered with the DragDropMgr.
21618          * This is executed in DragDrop.init()
21619          * @method regDragDrop
21620          * @param {DragDrop} oDD the DragDrop object to register
21621          * @param {String} sGroup the name of the group this element belongs to
21622          * @static
21623          */
21624         regDragDrop: function(oDD, sGroup) {
21625             if (!this.initialized) { this.init(); }
21626
21627             if (!this.ids[sGroup]) {
21628                 this.ids[sGroup] = {};
21629             }
21630             this.ids[sGroup][oDD.id] = oDD;
21631         },
21632
21633         /**
21634          * Removes the supplied dd instance from the supplied group. Executed
21635          * by DragDrop.removeFromGroup, so don't call this function directly.
21636          * @method removeDDFromGroup
21637          * @private
21638          * @static
21639          */
21640         removeDDFromGroup: function(oDD, sGroup) {
21641             if (!this.ids[sGroup]) {
21642                 this.ids[sGroup] = {};
21643             }
21644
21645             var obj = this.ids[sGroup];
21646             if (obj && obj[oDD.id]) {
21647                 delete obj[oDD.id];
21648             }
21649         },
21650
21651         /**
21652          * Unregisters a drag and drop item.  This is executed in
21653          * DragDrop.unreg, use that method instead of calling this directly.
21654          * @method _remove
21655          * @private
21656          * @static
21657          */
21658         _remove: function(oDD) {
21659             for (var g in oDD.groups) {
21660                 if (g && this.ids[g][oDD.id]) {
21661                     delete this.ids[g][oDD.id];
21662                 }
21663             }
21664             delete this.handleIds[oDD.id];
21665         },
21666
21667         /**
21668          * Each DragDrop handle element must be registered.  This is done
21669          * automatically when executing DragDrop.setHandleElId()
21670          * @method regHandle
21671          * @param {String} sDDId the DragDrop id this element is a handle for
21672          * @param {String} sHandleId the id of the element that is the drag
21673          * handle
21674          * @static
21675          */
21676         regHandle: function(sDDId, sHandleId) {
21677             if (!this.handleIds[sDDId]) {
21678                 this.handleIds[sDDId] = {};
21679             }
21680             this.handleIds[sDDId][sHandleId] = sHandleId;
21681         },
21682
21683         /**
21684          * Utility function to determine if a given element has been
21685          * registered as a drag drop item.
21686          * @method isDragDrop
21687          * @param {String} id the element id to check
21688          * @return {boolean} true if this element is a DragDrop item,
21689          * false otherwise
21690          * @static
21691          */
21692         isDragDrop: function(id) {
21693             return ( this.getDDById(id) ) ? true : false;
21694         },
21695
21696         /**
21697          * Returns the drag and drop instances that are in all groups the
21698          * passed in instance belongs to.
21699          * @method getRelated
21700          * @param {DragDrop} p_oDD the obj to get related data for
21701          * @param {boolean} bTargetsOnly if true, only return targetable objs
21702          * @return {DragDrop[]} the related instances
21703          * @static
21704          */
21705         getRelated: function(p_oDD, bTargetsOnly) {
21706             var oDDs = [];
21707             for (var i in p_oDD.groups) {
21708                 for (j in this.ids[i]) {
21709                     var dd = this.ids[i][j];
21710                     if (! this.isTypeOfDD(dd)) {
21711                         continue;
21712                     }
21713                     if (!bTargetsOnly || dd.isTarget) {
21714                         oDDs[oDDs.length] = dd;
21715                     }
21716                 }
21717             }
21718
21719             return oDDs;
21720         },
21721
21722         /**
21723          * Returns true if the specified dd target is a legal target for
21724          * the specifice drag obj
21725          * @method isLegalTarget
21726          * @param {DragDrop} the drag obj
21727          * @param {DragDrop} the target
21728          * @return {boolean} true if the target is a legal target for the
21729          * dd obj
21730          * @static
21731          */
21732         isLegalTarget: function (oDD, oTargetDD) {
21733             var targets = this.getRelated(oDD, true);
21734             for (var i=0, len=targets.length;i<len;++i) {
21735                 if (targets[i].id == oTargetDD.id) {
21736                     return true;
21737                 }
21738             }
21739
21740             return false;
21741         },
21742
21743         /**
21744          * My goal is to be able to transparently determine if an object is
21745          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21746          * returns "object", oDD.constructor.toString() always returns
21747          * "DragDrop" and not the name of the subclass.  So for now it just
21748          * evaluates a well-known variable in DragDrop.
21749          * @method isTypeOfDD
21750          * @param {Object} the object to evaluate
21751          * @return {boolean} true if typeof oDD = DragDrop
21752          * @static
21753          */
21754         isTypeOfDD: function (oDD) {
21755             return (oDD && oDD.__ygDragDrop);
21756         },
21757
21758         /**
21759          * Utility function to determine if a given element has been
21760          * registered as a drag drop handle for the given Drag Drop object.
21761          * @method isHandle
21762          * @param {String} id the element id to check
21763          * @return {boolean} true if this element is a DragDrop handle, false
21764          * otherwise
21765          * @static
21766          */
21767         isHandle: function(sDDId, sHandleId) {
21768             return ( this.handleIds[sDDId] &&
21769                             this.handleIds[sDDId][sHandleId] );
21770         },
21771
21772         /**
21773          * Returns the DragDrop instance for a given id
21774          * @method getDDById
21775          * @param {String} id the id of the DragDrop object
21776          * @return {DragDrop} the drag drop object, null if it is not found
21777          * @static
21778          */
21779         getDDById: function(id) {
21780             for (var i in this.ids) {
21781                 if (this.ids[i][id]) {
21782                     return this.ids[i][id];
21783                 }
21784             }
21785             return null;
21786         },
21787
21788         /**
21789          * Fired after a registered DragDrop object gets the mousedown event.
21790          * Sets up the events required to track the object being dragged
21791          * @method handleMouseDown
21792          * @param {Event} e the event
21793          * @param oDD the DragDrop object being dragged
21794          * @private
21795          * @static
21796          */
21797         handleMouseDown: function(e, oDD) {
21798             if(Roo.QuickTips){
21799                 Roo.QuickTips.disable();
21800             }
21801             this.currentTarget = e.getTarget();
21802
21803             this.dragCurrent = oDD;
21804
21805             var el = oDD.getEl();
21806
21807             // track start position
21808             this.startX = e.getPageX();
21809             this.startY = e.getPageY();
21810
21811             this.deltaX = this.startX - el.offsetLeft;
21812             this.deltaY = this.startY - el.offsetTop;
21813
21814             this.dragThreshMet = false;
21815
21816             this.clickTimeout = setTimeout(
21817                     function() {
21818                         var DDM = Roo.dd.DDM;
21819                         DDM.startDrag(DDM.startX, DDM.startY);
21820                     },
21821                     this.clickTimeThresh );
21822         },
21823
21824         /**
21825          * Fired when either the drag pixel threshol or the mousedown hold
21826          * time threshold has been met.
21827          * @method startDrag
21828          * @param x {int} the X position of the original mousedown
21829          * @param y {int} the Y position of the original mousedown
21830          * @static
21831          */
21832         startDrag: function(x, y) {
21833             clearTimeout(this.clickTimeout);
21834             if (this.dragCurrent) {
21835                 this.dragCurrent.b4StartDrag(x, y);
21836                 this.dragCurrent.startDrag(x, y);
21837             }
21838             this.dragThreshMet = true;
21839         },
21840
21841         /**
21842          * Internal function to handle the mouseup event.  Will be invoked
21843          * from the context of the document.
21844          * @method handleMouseUp
21845          * @param {Event} e the event
21846          * @private
21847          * @static
21848          */
21849         handleMouseUp: function(e) {
21850
21851             if(Roo.QuickTips){
21852                 Roo.QuickTips.enable();
21853             }
21854             if (! this.dragCurrent) {
21855                 return;
21856             }
21857
21858             clearTimeout(this.clickTimeout);
21859
21860             if (this.dragThreshMet) {
21861                 this.fireEvents(e, true);
21862             } else {
21863             }
21864
21865             this.stopDrag(e);
21866
21867             this.stopEvent(e);
21868         },
21869
21870         /**
21871          * Utility to stop event propagation and event default, if these
21872          * features are turned on.
21873          * @method stopEvent
21874          * @param {Event} e the event as returned by this.getEvent()
21875          * @static
21876          */
21877         stopEvent: function(e){
21878             if(this.stopPropagation) {
21879                 e.stopPropagation();
21880             }
21881
21882             if (this.preventDefault) {
21883                 e.preventDefault();
21884             }
21885         },
21886
21887         /**
21888          * Internal function to clean up event handlers after the drag
21889          * operation is complete
21890          * @method stopDrag
21891          * @param {Event} e the event
21892          * @private
21893          * @static
21894          */
21895         stopDrag: function(e) {
21896             // Fire the drag end event for the item that was dragged
21897             if (this.dragCurrent) {
21898                 if (this.dragThreshMet) {
21899                     this.dragCurrent.b4EndDrag(e);
21900                     this.dragCurrent.endDrag(e);
21901                 }
21902
21903                 this.dragCurrent.onMouseUp(e);
21904             }
21905
21906             this.dragCurrent = null;
21907             this.dragOvers = {};
21908         },
21909
21910         /**
21911          * Internal function to handle the mousemove event.  Will be invoked
21912          * from the context of the html element.
21913          *
21914          * @TODO figure out what we can do about mouse events lost when the
21915          * user drags objects beyond the window boundary.  Currently we can
21916          * detect this in internet explorer by verifying that the mouse is
21917          * down during the mousemove event.  Firefox doesn't give us the
21918          * button state on the mousemove event.
21919          * @method handleMouseMove
21920          * @param {Event} e the event
21921          * @private
21922          * @static
21923          */
21924         handleMouseMove: function(e) {
21925             if (! this.dragCurrent) {
21926                 return true;
21927             }
21928
21929             // var button = e.which || e.button;
21930
21931             // check for IE mouseup outside of page boundary
21932             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21933                 this.stopEvent(e);
21934                 return this.handleMouseUp(e);
21935             }
21936
21937             if (!this.dragThreshMet) {
21938                 var diffX = Math.abs(this.startX - e.getPageX());
21939                 var diffY = Math.abs(this.startY - e.getPageY());
21940                 if (diffX > this.clickPixelThresh ||
21941                             diffY > this.clickPixelThresh) {
21942                     this.startDrag(this.startX, this.startY);
21943                 }
21944             }
21945
21946             if (this.dragThreshMet) {
21947                 this.dragCurrent.b4Drag(e);
21948                 this.dragCurrent.onDrag(e);
21949                 if(!this.dragCurrent.moveOnly){
21950                     this.fireEvents(e, false);
21951                 }
21952             }
21953
21954             this.stopEvent(e);
21955
21956             return true;
21957         },
21958
21959         /**
21960          * Iterates over all of the DragDrop elements to find ones we are
21961          * hovering over or dropping on
21962          * @method fireEvents
21963          * @param {Event} e the event
21964          * @param {boolean} isDrop is this a drop op or a mouseover op?
21965          * @private
21966          * @static
21967          */
21968         fireEvents: function(e, isDrop) {
21969             var dc = this.dragCurrent;
21970
21971             // If the user did the mouse up outside of the window, we could
21972             // get here even though we have ended the drag.
21973             if (!dc || dc.isLocked()) {
21974                 return;
21975             }
21976
21977             var pt = e.getPoint();
21978
21979             // cache the previous dragOver array
21980             var oldOvers = [];
21981
21982             var outEvts   = [];
21983             var overEvts  = [];
21984             var dropEvts  = [];
21985             var enterEvts = [];
21986
21987             // Check to see if the object(s) we were hovering over is no longer
21988             // being hovered over so we can fire the onDragOut event
21989             for (var i in this.dragOvers) {
21990
21991                 var ddo = this.dragOvers[i];
21992
21993                 if (! this.isTypeOfDD(ddo)) {
21994                     continue;
21995                 }
21996
21997                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21998                     outEvts.push( ddo );
21999                 }
22000
22001                 oldOvers[i] = true;
22002                 delete this.dragOvers[i];
22003             }
22004
22005             for (var sGroup in dc.groups) {
22006
22007                 if ("string" != typeof sGroup) {
22008                     continue;
22009                 }
22010
22011                 for (i in this.ids[sGroup]) {
22012                     var oDD = this.ids[sGroup][i];
22013                     if (! this.isTypeOfDD(oDD)) {
22014                         continue;
22015                     }
22016
22017                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22018                         if (this.isOverTarget(pt, oDD, this.mode)) {
22019                             // look for drop interactions
22020                             if (isDrop) {
22021                                 dropEvts.push( oDD );
22022                             // look for drag enter and drag over interactions
22023                             } else {
22024
22025                                 // initial drag over: dragEnter fires
22026                                 if (!oldOvers[oDD.id]) {
22027                                     enterEvts.push( oDD );
22028                                 // subsequent drag overs: dragOver fires
22029                                 } else {
22030                                     overEvts.push( oDD );
22031                                 }
22032
22033                                 this.dragOvers[oDD.id] = oDD;
22034                             }
22035                         }
22036                     }
22037                 }
22038             }
22039
22040             if (this.mode) {
22041                 if (outEvts.length) {
22042                     dc.b4DragOut(e, outEvts);
22043                     dc.onDragOut(e, outEvts);
22044                 }
22045
22046                 if (enterEvts.length) {
22047                     dc.onDragEnter(e, enterEvts);
22048                 }
22049
22050                 if (overEvts.length) {
22051                     dc.b4DragOver(e, overEvts);
22052                     dc.onDragOver(e, overEvts);
22053                 }
22054
22055                 if (dropEvts.length) {
22056                     dc.b4DragDrop(e, dropEvts);
22057                     dc.onDragDrop(e, dropEvts);
22058                 }
22059
22060             } else {
22061                 // fire dragout events
22062                 var len = 0;
22063                 for (i=0, len=outEvts.length; i<len; ++i) {
22064                     dc.b4DragOut(e, outEvts[i].id);
22065                     dc.onDragOut(e, outEvts[i].id);
22066                 }
22067
22068                 // fire enter events
22069                 for (i=0,len=enterEvts.length; i<len; ++i) {
22070                     // dc.b4DragEnter(e, oDD.id);
22071                     dc.onDragEnter(e, enterEvts[i].id);
22072                 }
22073
22074                 // fire over events
22075                 for (i=0,len=overEvts.length; i<len; ++i) {
22076                     dc.b4DragOver(e, overEvts[i].id);
22077                     dc.onDragOver(e, overEvts[i].id);
22078                 }
22079
22080                 // fire drop events
22081                 for (i=0, len=dropEvts.length; i<len; ++i) {
22082                     dc.b4DragDrop(e, dropEvts[i].id);
22083                     dc.onDragDrop(e, dropEvts[i].id);
22084                 }
22085
22086             }
22087
22088             // notify about a drop that did not find a target
22089             if (isDrop && !dropEvts.length) {
22090                 dc.onInvalidDrop(e);
22091             }
22092
22093         },
22094
22095         /**
22096          * Helper function for getting the best match from the list of drag
22097          * and drop objects returned by the drag and drop events when we are
22098          * in INTERSECT mode.  It returns either the first object that the
22099          * cursor is over, or the object that has the greatest overlap with
22100          * the dragged element.
22101          * @method getBestMatch
22102          * @param  {DragDrop[]} dds The array of drag and drop objects
22103          * targeted
22104          * @return {DragDrop}       The best single match
22105          * @static
22106          */
22107         getBestMatch: function(dds) {
22108             var winner = null;
22109             // Return null if the input is not what we expect
22110             //if (!dds || !dds.length || dds.length == 0) {
22111                // winner = null;
22112             // If there is only one item, it wins
22113             //} else if (dds.length == 1) {
22114
22115             var len = dds.length;
22116
22117             if (len == 1) {
22118                 winner = dds[0];
22119             } else {
22120                 // Loop through the targeted items
22121                 for (var i=0; i<len; ++i) {
22122                     var dd = dds[i];
22123                     // If the cursor is over the object, it wins.  If the
22124                     // cursor is over multiple matches, the first one we come
22125                     // to wins.
22126                     if (dd.cursorIsOver) {
22127                         winner = dd;
22128                         break;
22129                     // Otherwise the object with the most overlap wins
22130                     } else {
22131                         if (!winner ||
22132                             winner.overlap.getArea() < dd.overlap.getArea()) {
22133                             winner = dd;
22134                         }
22135                     }
22136                 }
22137             }
22138
22139             return winner;
22140         },
22141
22142         /**
22143          * Refreshes the cache of the top-left and bottom-right points of the
22144          * drag and drop objects in the specified group(s).  This is in the
22145          * format that is stored in the drag and drop instance, so typical
22146          * usage is:
22147          * <code>
22148          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22149          * </code>
22150          * Alternatively:
22151          * <code>
22152          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22153          * </code>
22154          * @TODO this really should be an indexed array.  Alternatively this
22155          * method could accept both.
22156          * @method refreshCache
22157          * @param {Object} groups an associative array of groups to refresh
22158          * @static
22159          */
22160         refreshCache: function(groups) {
22161             for (var sGroup in groups) {
22162                 if ("string" != typeof sGroup) {
22163                     continue;
22164                 }
22165                 for (var i in this.ids[sGroup]) {
22166                     var oDD = this.ids[sGroup][i];
22167
22168                     if (this.isTypeOfDD(oDD)) {
22169                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22170                         var loc = this.getLocation(oDD);
22171                         if (loc) {
22172                             this.locationCache[oDD.id] = loc;
22173                         } else {
22174                             delete this.locationCache[oDD.id];
22175                             // this will unregister the drag and drop object if
22176                             // the element is not in a usable state
22177                             // oDD.unreg();
22178                         }
22179                     }
22180                 }
22181             }
22182         },
22183
22184         /**
22185          * This checks to make sure an element exists and is in the DOM.  The
22186          * main purpose is to handle cases where innerHTML is used to remove
22187          * drag and drop objects from the DOM.  IE provides an 'unspecified
22188          * error' when trying to access the offsetParent of such an element
22189          * @method verifyEl
22190          * @param {HTMLElement} el the element to check
22191          * @return {boolean} true if the element looks usable
22192          * @static
22193          */
22194         verifyEl: function(el) {
22195             if (el) {
22196                 var parent;
22197                 if(Roo.isIE){
22198                     try{
22199                         parent = el.offsetParent;
22200                     }catch(e){}
22201                 }else{
22202                     parent = el.offsetParent;
22203                 }
22204                 if (parent) {
22205                     return true;
22206                 }
22207             }
22208
22209             return false;
22210         },
22211
22212         /**
22213          * Returns a Region object containing the drag and drop element's position
22214          * and size, including the padding configured for it
22215          * @method getLocation
22216          * @param {DragDrop} oDD the drag and drop object to get the
22217          *                       location for
22218          * @return {Roo.lib.Region} a Region object representing the total area
22219          *                             the element occupies, including any padding
22220          *                             the instance is configured for.
22221          * @static
22222          */
22223         getLocation: function(oDD) {
22224             if (! this.isTypeOfDD(oDD)) {
22225                 return null;
22226             }
22227
22228             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22229
22230             try {
22231                 pos= Roo.lib.Dom.getXY(el);
22232             } catch (e) { }
22233
22234             if (!pos) {
22235                 return null;
22236             }
22237
22238             x1 = pos[0];
22239             x2 = x1 + el.offsetWidth;
22240             y1 = pos[1];
22241             y2 = y1 + el.offsetHeight;
22242
22243             t = y1 - oDD.padding[0];
22244             r = x2 + oDD.padding[1];
22245             b = y2 + oDD.padding[2];
22246             l = x1 - oDD.padding[3];
22247
22248             return new Roo.lib.Region( t, r, b, l );
22249         },
22250
22251         /**
22252          * Checks the cursor location to see if it over the target
22253          * @method isOverTarget
22254          * @param {Roo.lib.Point} pt The point to evaluate
22255          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22256          * @return {boolean} true if the mouse is over the target
22257          * @private
22258          * @static
22259          */
22260         isOverTarget: function(pt, oTarget, intersect) {
22261             // use cache if available
22262             var loc = this.locationCache[oTarget.id];
22263             if (!loc || !this.useCache) {
22264                 loc = this.getLocation(oTarget);
22265                 this.locationCache[oTarget.id] = loc;
22266
22267             }
22268
22269             if (!loc) {
22270                 return false;
22271             }
22272
22273             oTarget.cursorIsOver = loc.contains( pt );
22274
22275             // DragDrop is using this as a sanity check for the initial mousedown
22276             // in this case we are done.  In POINT mode, if the drag obj has no
22277             // contraints, we are also done. Otherwise we need to evaluate the
22278             // location of the target as related to the actual location of the
22279             // dragged element.
22280             var dc = this.dragCurrent;
22281             if (!dc || !dc.getTargetCoord ||
22282                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22283                 return oTarget.cursorIsOver;
22284             }
22285
22286             oTarget.overlap = null;
22287
22288             // Get the current location of the drag element, this is the
22289             // location of the mouse event less the delta that represents
22290             // where the original mousedown happened on the element.  We
22291             // need to consider constraints and ticks as well.
22292             var pos = dc.getTargetCoord(pt.x, pt.y);
22293
22294             var el = dc.getDragEl();
22295             var curRegion = new Roo.lib.Region( pos.y,
22296                                                    pos.x + el.offsetWidth,
22297                                                    pos.y + el.offsetHeight,
22298                                                    pos.x );
22299
22300             var overlap = curRegion.intersect(loc);
22301
22302             if (overlap) {
22303                 oTarget.overlap = overlap;
22304                 return (intersect) ? true : oTarget.cursorIsOver;
22305             } else {
22306                 return false;
22307             }
22308         },
22309
22310         /**
22311          * unload event handler
22312          * @method _onUnload
22313          * @private
22314          * @static
22315          */
22316         _onUnload: function(e, me) {
22317             Roo.dd.DragDropMgr.unregAll();
22318         },
22319
22320         /**
22321          * Cleans up the drag and drop events and objects.
22322          * @method unregAll
22323          * @private
22324          * @static
22325          */
22326         unregAll: function() {
22327
22328             if (this.dragCurrent) {
22329                 this.stopDrag();
22330                 this.dragCurrent = null;
22331             }
22332
22333             this._execOnAll("unreg", []);
22334
22335             for (i in this.elementCache) {
22336                 delete this.elementCache[i];
22337             }
22338
22339             this.elementCache = {};
22340             this.ids = {};
22341         },
22342
22343         /**
22344          * A cache of DOM elements
22345          * @property elementCache
22346          * @private
22347          * @static
22348          */
22349         elementCache: {},
22350
22351         /**
22352          * Get the wrapper for the DOM element specified
22353          * @method getElWrapper
22354          * @param {String} id the id of the element to get
22355          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22356          * @private
22357          * @deprecated This wrapper isn't that useful
22358          * @static
22359          */
22360         getElWrapper: function(id) {
22361             var oWrapper = this.elementCache[id];
22362             if (!oWrapper || !oWrapper.el) {
22363                 oWrapper = this.elementCache[id] =
22364                     new this.ElementWrapper(Roo.getDom(id));
22365             }
22366             return oWrapper;
22367         },
22368
22369         /**
22370          * Returns the actual DOM element
22371          * @method getElement
22372          * @param {String} id the id of the elment to get
22373          * @return {Object} The element
22374          * @deprecated use Roo.getDom instead
22375          * @static
22376          */
22377         getElement: function(id) {
22378             return Roo.getDom(id);
22379         },
22380
22381         /**
22382          * Returns the style property for the DOM element (i.e.,
22383          * document.getElById(id).style)
22384          * @method getCss
22385          * @param {String} id the id of the elment to get
22386          * @return {Object} The style property of the element
22387          * @deprecated use Roo.getDom instead
22388          * @static
22389          */
22390         getCss: function(id) {
22391             var el = Roo.getDom(id);
22392             return (el) ? el.style : null;
22393         },
22394
22395         /**
22396          * Inner class for cached elements
22397          * @class DragDropMgr.ElementWrapper
22398          * @for DragDropMgr
22399          * @private
22400          * @deprecated
22401          */
22402         ElementWrapper: function(el) {
22403                 /**
22404                  * The element
22405                  * @property el
22406                  */
22407                 this.el = el || null;
22408                 /**
22409                  * The element id
22410                  * @property id
22411                  */
22412                 this.id = this.el && el.id;
22413                 /**
22414                  * A reference to the style property
22415                  * @property css
22416                  */
22417                 this.css = this.el && el.style;
22418             },
22419
22420         /**
22421          * Returns the X position of an html element
22422          * @method getPosX
22423          * @param el the element for which to get the position
22424          * @return {int} the X coordinate
22425          * @for DragDropMgr
22426          * @deprecated use Roo.lib.Dom.getX instead
22427          * @static
22428          */
22429         getPosX: function(el) {
22430             return Roo.lib.Dom.getX(el);
22431         },
22432
22433         /**
22434          * Returns the Y position of an html element
22435          * @method getPosY
22436          * @param el the element for which to get the position
22437          * @return {int} the Y coordinate
22438          * @deprecated use Roo.lib.Dom.getY instead
22439          * @static
22440          */
22441         getPosY: function(el) {
22442             return Roo.lib.Dom.getY(el);
22443         },
22444
22445         /**
22446          * Swap two nodes.  In IE, we use the native method, for others we
22447          * emulate the IE behavior
22448          * @method swapNode
22449          * @param n1 the first node to swap
22450          * @param n2 the other node to swap
22451          * @static
22452          */
22453         swapNode: function(n1, n2) {
22454             if (n1.swapNode) {
22455                 n1.swapNode(n2);
22456             } else {
22457                 var p = n2.parentNode;
22458                 var s = n2.nextSibling;
22459
22460                 if (s == n1) {
22461                     p.insertBefore(n1, n2);
22462                 } else if (n2 == n1.nextSibling) {
22463                     p.insertBefore(n2, n1);
22464                 } else {
22465                     n1.parentNode.replaceChild(n2, n1);
22466                     p.insertBefore(n1, s);
22467                 }
22468             }
22469         },
22470
22471         /**
22472          * Returns the current scroll position
22473          * @method getScroll
22474          * @private
22475          * @static
22476          */
22477         getScroll: function () {
22478             var t, l, dde=document.documentElement, db=document.body;
22479             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22480                 t = dde.scrollTop;
22481                 l = dde.scrollLeft;
22482             } else if (db) {
22483                 t = db.scrollTop;
22484                 l = db.scrollLeft;
22485             } else {
22486
22487             }
22488             return { top: t, left: l };
22489         },
22490
22491         /**
22492          * Returns the specified element style property
22493          * @method getStyle
22494          * @param {HTMLElement} el          the element
22495          * @param {string}      styleProp   the style property
22496          * @return {string} The value of the style property
22497          * @deprecated use Roo.lib.Dom.getStyle
22498          * @static
22499          */
22500         getStyle: function(el, styleProp) {
22501             return Roo.fly(el).getStyle(styleProp);
22502         },
22503
22504         /**
22505          * Gets the scrollTop
22506          * @method getScrollTop
22507          * @return {int} the document's scrollTop
22508          * @static
22509          */
22510         getScrollTop: function () { return this.getScroll().top; },
22511
22512         /**
22513          * Gets the scrollLeft
22514          * @method getScrollLeft
22515          * @return {int} the document's scrollTop
22516          * @static
22517          */
22518         getScrollLeft: function () { return this.getScroll().left; },
22519
22520         /**
22521          * Sets the x/y position of an element to the location of the
22522          * target element.
22523          * @method moveToEl
22524          * @param {HTMLElement} moveEl      The element to move
22525          * @param {HTMLElement} targetEl    The position reference element
22526          * @static
22527          */
22528         moveToEl: function (moveEl, targetEl) {
22529             var aCoord = Roo.lib.Dom.getXY(targetEl);
22530             Roo.lib.Dom.setXY(moveEl, aCoord);
22531         },
22532
22533         /**
22534          * Numeric array sort function
22535          * @method numericSort
22536          * @static
22537          */
22538         numericSort: function(a, b) { return (a - b); },
22539
22540         /**
22541          * Internal counter
22542          * @property _timeoutCount
22543          * @private
22544          * @static
22545          */
22546         _timeoutCount: 0,
22547
22548         /**
22549          * Trying to make the load order less important.  Without this we get
22550          * an error if this file is loaded before the Event Utility.
22551          * @method _addListeners
22552          * @private
22553          * @static
22554          */
22555         _addListeners: function() {
22556             var DDM = Roo.dd.DDM;
22557             if ( Roo.lib.Event && document ) {
22558                 DDM._onLoad();
22559             } else {
22560                 if (DDM._timeoutCount > 2000) {
22561                 } else {
22562                     setTimeout(DDM._addListeners, 10);
22563                     if (document && document.body) {
22564                         DDM._timeoutCount += 1;
22565                     }
22566                 }
22567             }
22568         },
22569
22570         /**
22571          * Recursively searches the immediate parent and all child nodes for
22572          * the handle element in order to determine wheter or not it was
22573          * clicked.
22574          * @method handleWasClicked
22575          * @param node the html element to inspect
22576          * @static
22577          */
22578         handleWasClicked: function(node, id) {
22579             if (this.isHandle(id, node.id)) {
22580                 return true;
22581             } else {
22582                 // check to see if this is a text node child of the one we want
22583                 var p = node.parentNode;
22584
22585                 while (p) {
22586                     if (this.isHandle(id, p.id)) {
22587                         return true;
22588                     } else {
22589                         p = p.parentNode;
22590                     }
22591                 }
22592             }
22593
22594             return false;
22595         }
22596
22597     };
22598
22599 }();
22600
22601 // shorter alias, save a few bytes
22602 Roo.dd.DDM = Roo.dd.DragDropMgr;
22603 Roo.dd.DDM._addListeners();
22604
22605 }/*
22606  * Based on:
22607  * Ext JS Library 1.1.1
22608  * Copyright(c) 2006-2007, Ext JS, LLC.
22609  *
22610  * Originally Released Under LGPL - original licence link has changed is not relivant.
22611  *
22612  * Fork - LGPL
22613  * <script type="text/javascript">
22614  */
22615
22616 /**
22617  * @class Roo.dd.DD
22618  * A DragDrop implementation where the linked element follows the
22619  * mouse cursor during a drag.
22620  * @extends Roo.dd.DragDrop
22621  * @constructor
22622  * @param {String} id the id of the linked element
22623  * @param {String} sGroup the group of related DragDrop items
22624  * @param {object} config an object containing configurable attributes
22625  *                Valid properties for DD:
22626  *                    scroll
22627  */
22628 Roo.dd.DD = function(id, sGroup, config) {
22629     if (id) {
22630         this.init(id, sGroup, config);
22631     }
22632 };
22633
22634 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22635
22636     /**
22637      * When set to true, the utility automatically tries to scroll the browser
22638      * window wehn a drag and drop element is dragged near the viewport boundary.
22639      * Defaults to true.
22640      * @property scroll
22641      * @type boolean
22642      */
22643     scroll: true,
22644
22645     /**
22646      * Sets the pointer offset to the distance between the linked element's top
22647      * left corner and the location the element was clicked
22648      * @method autoOffset
22649      * @param {int} iPageX the X coordinate of the click
22650      * @param {int} iPageY the Y coordinate of the click
22651      */
22652     autoOffset: function(iPageX, iPageY) {
22653         var x = iPageX - this.startPageX;
22654         var y = iPageY - this.startPageY;
22655         this.setDelta(x, y);
22656     },
22657
22658     /**
22659      * Sets the pointer offset.  You can call this directly to force the
22660      * offset to be in a particular location (e.g., pass in 0,0 to set it
22661      * to the center of the object)
22662      * @method setDelta
22663      * @param {int} iDeltaX the distance from the left
22664      * @param {int} iDeltaY the distance from the top
22665      */
22666     setDelta: function(iDeltaX, iDeltaY) {
22667         this.deltaX = iDeltaX;
22668         this.deltaY = iDeltaY;
22669     },
22670
22671     /**
22672      * Sets the drag element to the location of the mousedown or click event,
22673      * maintaining the cursor location relative to the location on the element
22674      * that was clicked.  Override this if you want to place the element in a
22675      * location other than where the cursor is.
22676      * @method setDragElPos
22677      * @param {int} iPageX the X coordinate of the mousedown or drag event
22678      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22679      */
22680     setDragElPos: function(iPageX, iPageY) {
22681         // the first time we do this, we are going to check to make sure
22682         // the element has css positioning
22683
22684         var el = this.getDragEl();
22685         this.alignElWithMouse(el, iPageX, iPageY);
22686     },
22687
22688     /**
22689      * Sets the element to the location of the mousedown or click event,
22690      * maintaining the cursor location relative to the location on the element
22691      * that was clicked.  Override this if you want to place the element in a
22692      * location other than where the cursor is.
22693      * @method alignElWithMouse
22694      * @param {HTMLElement} el the element to move
22695      * @param {int} iPageX the X coordinate of the mousedown or drag event
22696      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22697      */
22698     alignElWithMouse: function(el, iPageX, iPageY) {
22699         var oCoord = this.getTargetCoord(iPageX, iPageY);
22700         var fly = el.dom ? el : Roo.fly(el);
22701         if (!this.deltaSetXY) {
22702             var aCoord = [oCoord.x, oCoord.y];
22703             fly.setXY(aCoord);
22704             var newLeft = fly.getLeft(true);
22705             var newTop  = fly.getTop(true);
22706             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22707         } else {
22708             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22709         }
22710
22711         this.cachePosition(oCoord.x, oCoord.y);
22712         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22713         return oCoord;
22714     },
22715
22716     /**
22717      * Saves the most recent position so that we can reset the constraints and
22718      * tick marks on-demand.  We need to know this so that we can calculate the
22719      * number of pixels the element is offset from its original position.
22720      * @method cachePosition
22721      * @param iPageX the current x position (optional, this just makes it so we
22722      * don't have to look it up again)
22723      * @param iPageY the current y position (optional, this just makes it so we
22724      * don't have to look it up again)
22725      */
22726     cachePosition: function(iPageX, iPageY) {
22727         if (iPageX) {
22728             this.lastPageX = iPageX;
22729             this.lastPageY = iPageY;
22730         } else {
22731             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22732             this.lastPageX = aCoord[0];
22733             this.lastPageY = aCoord[1];
22734         }
22735     },
22736
22737     /**
22738      * Auto-scroll the window if the dragged object has been moved beyond the
22739      * visible window boundary.
22740      * @method autoScroll
22741      * @param {int} x the drag element's x position
22742      * @param {int} y the drag element's y position
22743      * @param {int} h the height of the drag element
22744      * @param {int} w the width of the drag element
22745      * @private
22746      */
22747     autoScroll: function(x, y, h, w) {
22748
22749         if (this.scroll) {
22750             // The client height
22751             var clientH = Roo.lib.Dom.getViewWidth();
22752
22753             // The client width
22754             var clientW = Roo.lib.Dom.getViewHeight();
22755
22756             // The amt scrolled down
22757             var st = this.DDM.getScrollTop();
22758
22759             // The amt scrolled right
22760             var sl = this.DDM.getScrollLeft();
22761
22762             // Location of the bottom of the element
22763             var bot = h + y;
22764
22765             // Location of the right of the element
22766             var right = w + x;
22767
22768             // The distance from the cursor to the bottom of the visible area,
22769             // adjusted so that we don't scroll if the cursor is beyond the
22770             // element drag constraints
22771             var toBot = (clientH + st - y - this.deltaY);
22772
22773             // The distance from the cursor to the right of the visible area
22774             var toRight = (clientW + sl - x - this.deltaX);
22775
22776
22777             // How close to the edge the cursor must be before we scroll
22778             // var thresh = (document.all) ? 100 : 40;
22779             var thresh = 40;
22780
22781             // How many pixels to scroll per autoscroll op.  This helps to reduce
22782             // clunky scrolling. IE is more sensitive about this ... it needs this
22783             // value to be higher.
22784             var scrAmt = (document.all) ? 80 : 30;
22785
22786             // Scroll down if we are near the bottom of the visible page and the
22787             // obj extends below the crease
22788             if ( bot > clientH && toBot < thresh ) {
22789                 window.scrollTo(sl, st + scrAmt);
22790             }
22791
22792             // Scroll up if the window is scrolled down and the top of the object
22793             // goes above the top border
22794             if ( y < st && st > 0 && y - st < thresh ) {
22795                 window.scrollTo(sl, st - scrAmt);
22796             }
22797
22798             // Scroll right if the obj is beyond the right border and the cursor is
22799             // near the border.
22800             if ( right > clientW && toRight < thresh ) {
22801                 window.scrollTo(sl + scrAmt, st);
22802             }
22803
22804             // Scroll left if the window has been scrolled to the right and the obj
22805             // extends past the left border
22806             if ( x < sl && sl > 0 && x - sl < thresh ) {
22807                 window.scrollTo(sl - scrAmt, st);
22808             }
22809         }
22810     },
22811
22812     /**
22813      * Finds the location the element should be placed if we want to move
22814      * it to where the mouse location less the click offset would place us.
22815      * @method getTargetCoord
22816      * @param {int} iPageX the X coordinate of the click
22817      * @param {int} iPageY the Y coordinate of the click
22818      * @return an object that contains the coordinates (Object.x and Object.y)
22819      * @private
22820      */
22821     getTargetCoord: function(iPageX, iPageY) {
22822
22823
22824         var x = iPageX - this.deltaX;
22825         var y = iPageY - this.deltaY;
22826
22827         if (this.constrainX) {
22828             if (x < this.minX) { x = this.minX; }
22829             if (x > this.maxX) { x = this.maxX; }
22830         }
22831
22832         if (this.constrainY) {
22833             if (y < this.minY) { y = this.minY; }
22834             if (y > this.maxY) { y = this.maxY; }
22835         }
22836
22837         x = this.getTick(x, this.xTicks);
22838         y = this.getTick(y, this.yTicks);
22839
22840
22841         return {x:x, y:y};
22842     },
22843
22844     /*
22845      * Sets up config options specific to this class. Overrides
22846      * Roo.dd.DragDrop, but all versions of this method through the
22847      * inheritance chain are called
22848      */
22849     applyConfig: function() {
22850         Roo.dd.DD.superclass.applyConfig.call(this);
22851         this.scroll = (this.config.scroll !== false);
22852     },
22853
22854     /*
22855      * Event that fires prior to the onMouseDown event.  Overrides
22856      * Roo.dd.DragDrop.
22857      */
22858     b4MouseDown: function(e) {
22859         // this.resetConstraints();
22860         this.autoOffset(e.getPageX(),
22861                             e.getPageY());
22862     },
22863
22864     /*
22865      * Event that fires prior to the onDrag event.  Overrides
22866      * Roo.dd.DragDrop.
22867      */
22868     b4Drag: function(e) {
22869         this.setDragElPos(e.getPageX(),
22870                             e.getPageY());
22871     },
22872
22873     toString: function() {
22874         return ("DD " + this.id);
22875     }
22876
22877     //////////////////////////////////////////////////////////////////////////
22878     // Debugging ygDragDrop events that can be overridden
22879     //////////////////////////////////////////////////////////////////////////
22880     /*
22881     startDrag: function(x, y) {
22882     },
22883
22884     onDrag: function(e) {
22885     },
22886
22887     onDragEnter: function(e, id) {
22888     },
22889
22890     onDragOver: function(e, id) {
22891     },
22892
22893     onDragOut: function(e, id) {
22894     },
22895
22896     onDragDrop: function(e, id) {
22897     },
22898
22899     endDrag: function(e) {
22900     }
22901
22902     */
22903
22904 });/*
22905  * Based on:
22906  * Ext JS Library 1.1.1
22907  * Copyright(c) 2006-2007, Ext JS, LLC.
22908  *
22909  * Originally Released Under LGPL - original licence link has changed is not relivant.
22910  *
22911  * Fork - LGPL
22912  * <script type="text/javascript">
22913  */
22914
22915 /**
22916  * @class Roo.dd.DDProxy
22917  * A DragDrop implementation that inserts an empty, bordered div into
22918  * the document that follows the cursor during drag operations.  At the time of
22919  * the click, the frame div is resized to the dimensions of the linked html
22920  * element, and moved to the exact location of the linked element.
22921  *
22922  * References to the "frame" element refer to the single proxy element that
22923  * was created to be dragged in place of all DDProxy elements on the
22924  * page.
22925  *
22926  * @extends Roo.dd.DD
22927  * @constructor
22928  * @param {String} id the id of the linked html element
22929  * @param {String} sGroup the group of related DragDrop objects
22930  * @param {object} config an object containing configurable attributes
22931  *                Valid properties for DDProxy in addition to those in DragDrop:
22932  *                   resizeFrame, centerFrame, dragElId
22933  */
22934 Roo.dd.DDProxy = function(id, sGroup, config) {
22935     if (id) {
22936         this.init(id, sGroup, config);
22937         this.initFrame();
22938     }
22939 };
22940
22941 /**
22942  * The default drag frame div id
22943  * @property Roo.dd.DDProxy.dragElId
22944  * @type String
22945  * @static
22946  */
22947 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22948
22949 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22950
22951     /**
22952      * By default we resize the drag frame to be the same size as the element
22953      * we want to drag (this is to get the frame effect).  We can turn it off
22954      * if we want a different behavior.
22955      * @property resizeFrame
22956      * @type boolean
22957      */
22958     resizeFrame: true,
22959
22960     /**
22961      * By default the frame is positioned exactly where the drag element is, so
22962      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22963      * you do not have constraints on the obj is to have the drag frame centered
22964      * around the cursor.  Set centerFrame to true for this effect.
22965      * @property centerFrame
22966      * @type boolean
22967      */
22968     centerFrame: false,
22969
22970     /**
22971      * Creates the proxy element if it does not yet exist
22972      * @method createFrame
22973      */
22974     createFrame: function() {
22975         var self = this;
22976         var body = document.body;
22977
22978         if (!body || !body.firstChild) {
22979             setTimeout( function() { self.createFrame(); }, 50 );
22980             return;
22981         }
22982
22983         var div = this.getDragEl();
22984
22985         if (!div) {
22986             div    = document.createElement("div");
22987             div.id = this.dragElId;
22988             var s  = div.style;
22989
22990             s.position   = "absolute";
22991             s.visibility = "hidden";
22992             s.cursor     = "move";
22993             s.border     = "2px solid #aaa";
22994             s.zIndex     = 999;
22995
22996             // appendChild can blow up IE if invoked prior to the window load event
22997             // while rendering a table.  It is possible there are other scenarios
22998             // that would cause this to happen as well.
22999             body.insertBefore(div, body.firstChild);
23000         }
23001     },
23002
23003     /**
23004      * Initialization for the drag frame element.  Must be called in the
23005      * constructor of all subclasses
23006      * @method initFrame
23007      */
23008     initFrame: function() {
23009         this.createFrame();
23010     },
23011
23012     applyConfig: function() {
23013         Roo.dd.DDProxy.superclass.applyConfig.call(this);
23014
23015         this.resizeFrame = (this.config.resizeFrame !== false);
23016         this.centerFrame = (this.config.centerFrame);
23017         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23018     },
23019
23020     /**
23021      * Resizes the drag frame to the dimensions of the clicked object, positions
23022      * it over the object, and finally displays it
23023      * @method showFrame
23024      * @param {int} iPageX X click position
23025      * @param {int} iPageY Y click position
23026      * @private
23027      */
23028     showFrame: function(iPageX, iPageY) {
23029         var el = this.getEl();
23030         var dragEl = this.getDragEl();
23031         var s = dragEl.style;
23032
23033         this._resizeProxy();
23034
23035         if (this.centerFrame) {
23036             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23037                            Math.round(parseInt(s.height, 10)/2) );
23038         }
23039
23040         this.setDragElPos(iPageX, iPageY);
23041
23042         Roo.fly(dragEl).show();
23043     },
23044
23045     /**
23046      * The proxy is automatically resized to the dimensions of the linked
23047      * element when a drag is initiated, unless resizeFrame is set to false
23048      * @method _resizeProxy
23049      * @private
23050      */
23051     _resizeProxy: function() {
23052         if (this.resizeFrame) {
23053             var el = this.getEl();
23054             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23055         }
23056     },
23057
23058     // overrides Roo.dd.DragDrop
23059     b4MouseDown: function(e) {
23060         var x = e.getPageX();
23061         var y = e.getPageY();
23062         this.autoOffset(x, y);
23063         this.setDragElPos(x, y);
23064     },
23065
23066     // overrides Roo.dd.DragDrop
23067     b4StartDrag: function(x, y) {
23068         // show the drag frame
23069         this.showFrame(x, y);
23070     },
23071
23072     // overrides Roo.dd.DragDrop
23073     b4EndDrag: function(e) {
23074         Roo.fly(this.getDragEl()).hide();
23075     },
23076
23077     // overrides Roo.dd.DragDrop
23078     // By default we try to move the element to the last location of the frame.
23079     // This is so that the default behavior mirrors that of Roo.dd.DD.
23080     endDrag: function(e) {
23081
23082         var lel = this.getEl();
23083         var del = this.getDragEl();
23084
23085         // Show the drag frame briefly so we can get its position
23086         del.style.visibility = "";
23087
23088         this.beforeMove();
23089         // Hide the linked element before the move to get around a Safari
23090         // rendering bug.
23091         lel.style.visibility = "hidden";
23092         Roo.dd.DDM.moveToEl(lel, del);
23093         del.style.visibility = "hidden";
23094         lel.style.visibility = "";
23095
23096         this.afterDrag();
23097     },
23098
23099     beforeMove : function(){
23100
23101     },
23102
23103     afterDrag : function(){
23104
23105     },
23106
23107     toString: function() {
23108         return ("DDProxy " + this.id);
23109     }
23110
23111 });
23112 /*
23113  * Based on:
23114  * Ext JS Library 1.1.1
23115  * Copyright(c) 2006-2007, Ext JS, LLC.
23116  *
23117  * Originally Released Under LGPL - original licence link has changed is not relivant.
23118  *
23119  * Fork - LGPL
23120  * <script type="text/javascript">
23121  */
23122
23123  /**
23124  * @class Roo.dd.DDTarget
23125  * A DragDrop implementation that does not move, but can be a drop
23126  * target.  You would get the same result by simply omitting implementation
23127  * for the event callbacks, but this way we reduce the processing cost of the
23128  * event listener and the callbacks.
23129  * @extends Roo.dd.DragDrop
23130  * @constructor
23131  * @param {String} id the id of the element that is a drop target
23132  * @param {String} sGroup the group of related DragDrop objects
23133  * @param {object} config an object containing configurable attributes
23134  *                 Valid properties for DDTarget in addition to those in
23135  *                 DragDrop:
23136  *                    none
23137  */
23138 Roo.dd.DDTarget = function(id, sGroup, config) {
23139     if (id) {
23140         this.initTarget(id, sGroup, config);
23141     }
23142     if (config && (config.listeners || config.events)) { 
23143         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23144             listeners : config.listeners || {}, 
23145             events : config.events || {} 
23146         });    
23147     }
23148 };
23149
23150 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23151 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23152     toString: function() {
23153         return ("DDTarget " + this.id);
23154     }
23155 });
23156 /*
23157  * Based on:
23158  * Ext JS Library 1.1.1
23159  * Copyright(c) 2006-2007, Ext JS, LLC.
23160  *
23161  * Originally Released Under LGPL - original licence link has changed is not relivant.
23162  *
23163  * Fork - LGPL
23164  * <script type="text/javascript">
23165  */
23166  
23167
23168 /**
23169  * @class Roo.dd.ScrollManager
23170  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23171  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23172  * @static
23173  */
23174 Roo.dd.ScrollManager = function(){
23175     var ddm = Roo.dd.DragDropMgr;
23176     var els = {};
23177     var dragEl = null;
23178     var proc = {};
23179     
23180     
23181     
23182     var onStop = function(e){
23183         dragEl = null;
23184         clearProc();
23185     };
23186     
23187     var triggerRefresh = function(){
23188         if(ddm.dragCurrent){
23189              ddm.refreshCache(ddm.dragCurrent.groups);
23190         }
23191     };
23192     
23193     var doScroll = function(){
23194         if(ddm.dragCurrent){
23195             var dds = Roo.dd.ScrollManager;
23196             if(!dds.animate){
23197                 if(proc.el.scroll(proc.dir, dds.increment)){
23198                     triggerRefresh();
23199                 }
23200             }else{
23201                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23202             }
23203         }
23204     };
23205     
23206     var clearProc = function(){
23207         if(proc.id){
23208             clearInterval(proc.id);
23209         }
23210         proc.id = 0;
23211         proc.el = null;
23212         proc.dir = "";
23213     };
23214     
23215     var startProc = function(el, dir){
23216          Roo.log('scroll startproc');
23217         clearProc();
23218         proc.el = el;
23219         proc.dir = dir;
23220         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23221     };
23222     
23223     var onFire = function(e, isDrop){
23224        
23225         if(isDrop || !ddm.dragCurrent){ return; }
23226         var dds = Roo.dd.ScrollManager;
23227         if(!dragEl || dragEl != ddm.dragCurrent){
23228             dragEl = ddm.dragCurrent;
23229             // refresh regions on drag start
23230             dds.refreshCache();
23231         }
23232         
23233         var xy = Roo.lib.Event.getXY(e);
23234         var pt = new Roo.lib.Point(xy[0], xy[1]);
23235         for(var id in els){
23236             var el = els[id], r = el._region;
23237             if(r && r.contains(pt) && el.isScrollable()){
23238                 if(r.bottom - pt.y <= dds.thresh){
23239                     if(proc.el != el){
23240                         startProc(el, "down");
23241                     }
23242                     return;
23243                 }else if(r.right - pt.x <= dds.thresh){
23244                     if(proc.el != el){
23245                         startProc(el, "left");
23246                     }
23247                     return;
23248                 }else if(pt.y - r.top <= dds.thresh){
23249                     if(proc.el != el){
23250                         startProc(el, "up");
23251                     }
23252                     return;
23253                 }else if(pt.x - r.left <= dds.thresh){
23254                     if(proc.el != el){
23255                         startProc(el, "right");
23256                     }
23257                     return;
23258                 }
23259             }
23260         }
23261         clearProc();
23262     };
23263     
23264     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23265     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23266     
23267     return {
23268         /**
23269          * Registers new overflow element(s) to auto scroll
23270          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23271          */
23272         register : function(el){
23273             if(el instanceof Array){
23274                 for(var i = 0, len = el.length; i < len; i++) {
23275                         this.register(el[i]);
23276                 }
23277             }else{
23278                 el = Roo.get(el);
23279                 els[el.id] = el;
23280             }
23281             Roo.dd.ScrollManager.els = els;
23282         },
23283         
23284         /**
23285          * Unregisters overflow element(s) so they are no longer scrolled
23286          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23287          */
23288         unregister : function(el){
23289             if(el instanceof Array){
23290                 for(var i = 0, len = el.length; i < len; i++) {
23291                         this.unregister(el[i]);
23292                 }
23293             }else{
23294                 el = Roo.get(el);
23295                 delete els[el.id];
23296             }
23297         },
23298         
23299         /**
23300          * The number of pixels from the edge of a container the pointer needs to be to 
23301          * trigger scrolling (defaults to 25)
23302          * @type Number
23303          */
23304         thresh : 25,
23305         
23306         /**
23307          * The number of pixels to scroll in each scroll increment (defaults to 50)
23308          * @type Number
23309          */
23310         increment : 100,
23311         
23312         /**
23313          * The frequency of scrolls in milliseconds (defaults to 500)
23314          * @type Number
23315          */
23316         frequency : 500,
23317         
23318         /**
23319          * True to animate the scroll (defaults to true)
23320          * @type Boolean
23321          */
23322         animate: true,
23323         
23324         /**
23325          * The animation duration in seconds - 
23326          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23327          * @type Number
23328          */
23329         animDuration: .4,
23330         
23331         /**
23332          * Manually trigger a cache refresh.
23333          */
23334         refreshCache : function(){
23335             for(var id in els){
23336                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23337                     els[id]._region = els[id].getRegion();
23338                 }
23339             }
23340         }
23341     };
23342 }();/*
23343  * Based on:
23344  * Ext JS Library 1.1.1
23345  * Copyright(c) 2006-2007, Ext JS, LLC.
23346  *
23347  * Originally Released Under LGPL - original licence link has changed is not relivant.
23348  *
23349  * Fork - LGPL
23350  * <script type="text/javascript">
23351  */
23352  
23353
23354 /**
23355  * @class Roo.dd.Registry
23356  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23357  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23358  * @static
23359  */
23360 Roo.dd.Registry = function(){
23361     var elements = {}; 
23362     var handles = {}; 
23363     var autoIdSeed = 0;
23364
23365     var getId = function(el, autogen){
23366         if(typeof el == "string"){
23367             return el;
23368         }
23369         var id = el.id;
23370         if(!id && autogen !== false){
23371             id = "roodd-" + (++autoIdSeed);
23372             el.id = id;
23373         }
23374         return id;
23375     };
23376     
23377     return {
23378     /**
23379      * Register a drag drop element
23380      * @param {String|HTMLElement} element The id or DOM node to register
23381      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23382      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23383      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23384      * populated in the data object (if applicable):
23385      * <pre>
23386 Value      Description<br />
23387 ---------  ------------------------------------------<br />
23388 handles    Array of DOM nodes that trigger dragging<br />
23389            for the element being registered<br />
23390 isHandle   True if the element passed in triggers<br />
23391            dragging itself, else false
23392 </pre>
23393      */
23394         register : function(el, data){
23395             data = data || {};
23396             if(typeof el == "string"){
23397                 el = document.getElementById(el);
23398             }
23399             data.ddel = el;
23400             elements[getId(el)] = data;
23401             if(data.isHandle !== false){
23402                 handles[data.ddel.id] = data;
23403             }
23404             if(data.handles){
23405                 var hs = data.handles;
23406                 for(var i = 0, len = hs.length; i < len; i++){
23407                         handles[getId(hs[i])] = data;
23408                 }
23409             }
23410         },
23411
23412     /**
23413      * Unregister a drag drop element
23414      * @param {String|HTMLElement}  element The id or DOM node to unregister
23415      */
23416         unregister : function(el){
23417             var id = getId(el, false);
23418             var data = elements[id];
23419             if(data){
23420                 delete elements[id];
23421                 if(data.handles){
23422                     var hs = data.handles;
23423                     for(var i = 0, len = hs.length; i < len; i++){
23424                         delete handles[getId(hs[i], false)];
23425                     }
23426                 }
23427             }
23428         },
23429
23430     /**
23431      * Returns the handle registered for a DOM Node by id
23432      * @param {String|HTMLElement} id The DOM node or id to look up
23433      * @return {Object} handle The custom handle data
23434      */
23435         getHandle : function(id){
23436             if(typeof id != "string"){ // must be element?
23437                 id = id.id;
23438             }
23439             return handles[id];
23440         },
23441
23442     /**
23443      * Returns the handle that is registered for the DOM node that is the target of the event
23444      * @param {Event} e The event
23445      * @return {Object} handle The custom handle data
23446      */
23447         getHandleFromEvent : function(e){
23448             var t = Roo.lib.Event.getTarget(e);
23449             return t ? handles[t.id] : null;
23450         },
23451
23452     /**
23453      * Returns a custom data object that is registered for a DOM node by id
23454      * @param {String|HTMLElement} id The DOM node or id to look up
23455      * @return {Object} data The custom data
23456      */
23457         getTarget : function(id){
23458             if(typeof id != "string"){ // must be element?
23459                 id = id.id;
23460             }
23461             return elements[id];
23462         },
23463
23464     /**
23465      * Returns a custom data object that is registered for the DOM node that is the target of the event
23466      * @param {Event} e The event
23467      * @return {Object} data The custom data
23468      */
23469         getTargetFromEvent : function(e){
23470             var t = Roo.lib.Event.getTarget(e);
23471             return t ? elements[t.id] || handles[t.id] : null;
23472         }
23473     };
23474 }();/*
23475  * Based on:
23476  * Ext JS Library 1.1.1
23477  * Copyright(c) 2006-2007, Ext JS, LLC.
23478  *
23479  * Originally Released Under LGPL - original licence link has changed is not relivant.
23480  *
23481  * Fork - LGPL
23482  * <script type="text/javascript">
23483  */
23484  
23485
23486 /**
23487  * @class Roo.dd.StatusProxy
23488  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23489  * default drag proxy used by all Roo.dd components.
23490  * @constructor
23491  * @param {Object} config
23492  */
23493 Roo.dd.StatusProxy = function(config){
23494     Roo.apply(this, config);
23495     this.id = this.id || Roo.id();
23496     this.el = new Roo.Layer({
23497         dh: {
23498             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23499                 {tag: "div", cls: "x-dd-drop-icon"},
23500                 {tag: "div", cls: "x-dd-drag-ghost"}
23501             ]
23502         }, 
23503         shadow: !config || config.shadow !== false
23504     });
23505     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23506     this.dropStatus = this.dropNotAllowed;
23507 };
23508
23509 Roo.dd.StatusProxy.prototype = {
23510     /**
23511      * @cfg {String} dropAllowed
23512      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23513      */
23514     dropAllowed : "x-dd-drop-ok",
23515     /**
23516      * @cfg {String} dropNotAllowed
23517      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23518      */
23519     dropNotAllowed : "x-dd-drop-nodrop",
23520
23521     /**
23522      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23523      * over the current target element.
23524      * @param {String} cssClass The css class for the new drop status indicator image
23525      */
23526     setStatus : function(cssClass){
23527         cssClass = cssClass || this.dropNotAllowed;
23528         if(this.dropStatus != cssClass){
23529             this.el.replaceClass(this.dropStatus, cssClass);
23530             this.dropStatus = cssClass;
23531         }
23532     },
23533
23534     /**
23535      * Resets the status indicator to the default dropNotAllowed value
23536      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23537      */
23538     reset : function(clearGhost){
23539         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23540         this.dropStatus = this.dropNotAllowed;
23541         if(clearGhost){
23542             this.ghost.update("");
23543         }
23544     },
23545
23546     /**
23547      * Updates the contents of the ghost element
23548      * @param {String} html The html that will replace the current innerHTML of the ghost element
23549      */
23550     update : function(html){
23551         if(typeof html == "string"){
23552             this.ghost.update(html);
23553         }else{
23554             this.ghost.update("");
23555             html.style.margin = "0";
23556             this.ghost.dom.appendChild(html);
23557         }
23558         // ensure float = none set?? cant remember why though.
23559         var el = this.ghost.dom.firstChild;
23560                 if(el){
23561                         Roo.fly(el).setStyle('float', 'none');
23562                 }
23563     },
23564     
23565     /**
23566      * Returns the underlying proxy {@link Roo.Layer}
23567      * @return {Roo.Layer} el
23568     */
23569     getEl : function(){
23570         return this.el;
23571     },
23572
23573     /**
23574      * Returns the ghost element
23575      * @return {Roo.Element} el
23576      */
23577     getGhost : function(){
23578         return this.ghost;
23579     },
23580
23581     /**
23582      * Hides the proxy
23583      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23584      */
23585     hide : function(clear){
23586         this.el.hide();
23587         if(clear){
23588             this.reset(true);
23589         }
23590     },
23591
23592     /**
23593      * Stops the repair animation if it's currently running
23594      */
23595     stop : function(){
23596         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23597             this.anim.stop();
23598         }
23599     },
23600
23601     /**
23602      * Displays this proxy
23603      */
23604     show : function(){
23605         this.el.show();
23606     },
23607
23608     /**
23609      * Force the Layer to sync its shadow and shim positions to the element
23610      */
23611     sync : function(){
23612         this.el.sync();
23613     },
23614
23615     /**
23616      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23617      * invalid drop operation by the item being dragged.
23618      * @param {Array} xy The XY position of the element ([x, y])
23619      * @param {Function} callback The function to call after the repair is complete
23620      * @param {Object} scope The scope in which to execute the callback
23621      */
23622     repair : function(xy, callback, scope){
23623         this.callback = callback;
23624         this.scope = scope;
23625         if(xy && this.animRepair !== false){
23626             this.el.addClass("x-dd-drag-repair");
23627             this.el.hideUnders(true);
23628             this.anim = this.el.shift({
23629                 duration: this.repairDuration || .5,
23630                 easing: 'easeOut',
23631                 xy: xy,
23632                 stopFx: true,
23633                 callback: this.afterRepair,
23634                 scope: this
23635             });
23636         }else{
23637             this.afterRepair();
23638         }
23639     },
23640
23641     // private
23642     afterRepair : function(){
23643         this.hide(true);
23644         if(typeof this.callback == "function"){
23645             this.callback.call(this.scope || this);
23646         }
23647         this.callback = null;
23648         this.scope = null;
23649     }
23650 };/*
23651  * Based on:
23652  * Ext JS Library 1.1.1
23653  * Copyright(c) 2006-2007, Ext JS, LLC.
23654  *
23655  * Originally Released Under LGPL - original licence link has changed is not relivant.
23656  *
23657  * Fork - LGPL
23658  * <script type="text/javascript">
23659  */
23660
23661 /**
23662  * @class Roo.dd.DragSource
23663  * @extends Roo.dd.DDProxy
23664  * A simple class that provides the basic implementation needed to make any element draggable.
23665  * @constructor
23666  * @param {String/HTMLElement/Element} el The container element
23667  * @param {Object} config
23668  */
23669 Roo.dd.DragSource = function(el, config){
23670     this.el = Roo.get(el);
23671     this.dragData = {};
23672     
23673     Roo.apply(this, config);
23674     
23675     if(!this.proxy){
23676         this.proxy = new Roo.dd.StatusProxy();
23677     }
23678
23679     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23680           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23681     
23682     this.dragging = false;
23683 };
23684
23685 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23686     /**
23687      * @cfg {String} dropAllowed
23688      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23689      */
23690     dropAllowed : "x-dd-drop-ok",
23691     /**
23692      * @cfg {String} dropNotAllowed
23693      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23694      */
23695     dropNotAllowed : "x-dd-drop-nodrop",
23696
23697     /**
23698      * Returns the data object associated with this drag source
23699      * @return {Object} data An object containing arbitrary data
23700      */
23701     getDragData : function(e){
23702         return this.dragData;
23703     },
23704
23705     // private
23706     onDragEnter : function(e, id){
23707         var target = Roo.dd.DragDropMgr.getDDById(id);
23708         this.cachedTarget = target;
23709         if(this.beforeDragEnter(target, e, id) !== false){
23710             if(target.isNotifyTarget){
23711                 var status = target.notifyEnter(this, e, this.dragData);
23712                 this.proxy.setStatus(status);
23713             }else{
23714                 this.proxy.setStatus(this.dropAllowed);
23715             }
23716             
23717             if(this.afterDragEnter){
23718                 /**
23719                  * An empty function by default, but provided so that you can perform a custom action
23720                  * when the dragged item enters the drop target by providing an implementation.
23721                  * @param {Roo.dd.DragDrop} target The drop target
23722                  * @param {Event} e The event object
23723                  * @param {String} id The id of the dragged element
23724                  * @method afterDragEnter
23725                  */
23726                 this.afterDragEnter(target, e, id);
23727             }
23728         }
23729     },
23730
23731     /**
23732      * An empty function by default, but provided so that you can perform a custom action
23733      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23734      * @param {Roo.dd.DragDrop} target The drop target
23735      * @param {Event} e The event object
23736      * @param {String} id The id of the dragged element
23737      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23738      */
23739     beforeDragEnter : function(target, e, id){
23740         return true;
23741     },
23742
23743     // private
23744     alignElWithMouse: function() {
23745         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23746         this.proxy.sync();
23747     },
23748
23749     // private
23750     onDragOver : function(e, id){
23751         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23752         if(this.beforeDragOver(target, e, id) !== false){
23753             if(target.isNotifyTarget){
23754                 var status = target.notifyOver(this, e, this.dragData);
23755                 this.proxy.setStatus(status);
23756             }
23757
23758             if(this.afterDragOver){
23759                 /**
23760                  * An empty function by default, but provided so that you can perform a custom action
23761                  * while the dragged item is over the drop target by providing an implementation.
23762                  * @param {Roo.dd.DragDrop} target The drop target
23763                  * @param {Event} e The event object
23764                  * @param {String} id The id of the dragged element
23765                  * @method afterDragOver
23766                  */
23767                 this.afterDragOver(target, e, id);
23768             }
23769         }
23770     },
23771
23772     /**
23773      * An empty function by default, but provided so that you can perform a custom action
23774      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23775      * @param {Roo.dd.DragDrop} target The drop target
23776      * @param {Event} e The event object
23777      * @param {String} id The id of the dragged element
23778      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23779      */
23780     beforeDragOver : function(target, e, id){
23781         return true;
23782     },
23783
23784     // private
23785     onDragOut : function(e, id){
23786         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23787         if(this.beforeDragOut(target, e, id) !== false){
23788             if(target.isNotifyTarget){
23789                 target.notifyOut(this, e, this.dragData);
23790             }
23791             this.proxy.reset();
23792             if(this.afterDragOut){
23793                 /**
23794                  * An empty function by default, but provided so that you can perform a custom action
23795                  * after the dragged item is dragged out of the target without dropping.
23796                  * @param {Roo.dd.DragDrop} target The drop target
23797                  * @param {Event} e The event object
23798                  * @param {String} id The id of the dragged element
23799                  * @method afterDragOut
23800                  */
23801                 this.afterDragOut(target, e, id);
23802             }
23803         }
23804         this.cachedTarget = null;
23805     },
23806
23807     /**
23808      * An empty function by default, but provided so that you can perform a custom action before the dragged
23809      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23810      * @param {Roo.dd.DragDrop} target The drop target
23811      * @param {Event} e The event object
23812      * @param {String} id The id of the dragged element
23813      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23814      */
23815     beforeDragOut : function(target, e, id){
23816         return true;
23817     },
23818     
23819     // private
23820     onDragDrop : function(e, id){
23821         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23822         if(this.beforeDragDrop(target, e, id) !== false){
23823             if(target.isNotifyTarget){
23824                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23825                     this.onValidDrop(target, e, id);
23826                 }else{
23827                     this.onInvalidDrop(target, e, id);
23828                 }
23829             }else{
23830                 this.onValidDrop(target, e, id);
23831             }
23832             
23833             if(this.afterDragDrop){
23834                 /**
23835                  * An empty function by default, but provided so that you can perform a custom action
23836                  * after a valid drag drop has occurred by providing an implementation.
23837                  * @param {Roo.dd.DragDrop} target The drop target
23838                  * @param {Event} e The event object
23839                  * @param {String} id The id of the dropped element
23840                  * @method afterDragDrop
23841                  */
23842                 this.afterDragDrop(target, e, id);
23843             }
23844         }
23845         delete this.cachedTarget;
23846     },
23847
23848     /**
23849      * An empty function by default, but provided so that you can perform a custom action before the dragged
23850      * item is dropped onto the target and optionally cancel the onDragDrop.
23851      * @param {Roo.dd.DragDrop} target The drop target
23852      * @param {Event} e The event object
23853      * @param {String} id The id of the dragged element
23854      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23855      */
23856     beforeDragDrop : function(target, e, id){
23857         return true;
23858     },
23859
23860     // private
23861     onValidDrop : function(target, e, id){
23862         this.hideProxy();
23863         if(this.afterValidDrop){
23864             /**
23865              * An empty function by default, but provided so that you can perform a custom action
23866              * after a valid drop has occurred by providing an implementation.
23867              * @param {Object} target The target DD 
23868              * @param {Event} e The event object
23869              * @param {String} id The id of the dropped element
23870              * @method afterInvalidDrop
23871              */
23872             this.afterValidDrop(target, e, id);
23873         }
23874     },
23875
23876     // private
23877     getRepairXY : function(e, data){
23878         return this.el.getXY();  
23879     },
23880
23881     // private
23882     onInvalidDrop : function(target, e, id){
23883         this.beforeInvalidDrop(target, e, id);
23884         if(this.cachedTarget){
23885             if(this.cachedTarget.isNotifyTarget){
23886                 this.cachedTarget.notifyOut(this, e, this.dragData);
23887             }
23888             this.cacheTarget = null;
23889         }
23890         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23891
23892         if(this.afterInvalidDrop){
23893             /**
23894              * An empty function by default, but provided so that you can perform a custom action
23895              * after an invalid drop has occurred by providing an implementation.
23896              * @param {Event} e The event object
23897              * @param {String} id The id of the dropped element
23898              * @method afterInvalidDrop
23899              */
23900             this.afterInvalidDrop(e, id);
23901         }
23902     },
23903
23904     // private
23905     afterRepair : function(){
23906         if(Roo.enableFx){
23907             this.el.highlight(this.hlColor || "c3daf9");
23908         }
23909         this.dragging = false;
23910     },
23911
23912     /**
23913      * An empty function by default, but provided so that you can perform a custom action after an invalid
23914      * drop has occurred.
23915      * @param {Roo.dd.DragDrop} target The drop target
23916      * @param {Event} e The event object
23917      * @param {String} id The id of the dragged element
23918      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23919      */
23920     beforeInvalidDrop : function(target, e, id){
23921         return true;
23922     },
23923
23924     // private
23925     handleMouseDown : function(e){
23926         if(this.dragging) {
23927             return;
23928         }
23929         var data = this.getDragData(e);
23930         if(data && this.onBeforeDrag(data, e) !== false){
23931             this.dragData = data;
23932             this.proxy.stop();
23933             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23934         } 
23935     },
23936
23937     /**
23938      * An empty function by default, but provided so that you can perform a custom action before the initial
23939      * drag event begins and optionally cancel it.
23940      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23941      * @param {Event} e The event object
23942      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23943      */
23944     onBeforeDrag : function(data, e){
23945         return true;
23946     },
23947
23948     /**
23949      * An empty function by default, but provided so that you can perform a custom action once the initial
23950      * drag event has begun.  The drag cannot be canceled from this function.
23951      * @param {Number} x The x position of the click on the dragged object
23952      * @param {Number} y The y position of the click on the dragged object
23953      */
23954     onStartDrag : Roo.emptyFn,
23955
23956     // private - YUI override
23957     startDrag : function(x, y){
23958         this.proxy.reset();
23959         this.dragging = true;
23960         this.proxy.update("");
23961         this.onInitDrag(x, y);
23962         this.proxy.show();
23963     },
23964
23965     // private
23966     onInitDrag : function(x, y){
23967         var clone = this.el.dom.cloneNode(true);
23968         clone.id = Roo.id(); // prevent duplicate ids
23969         this.proxy.update(clone);
23970         this.onStartDrag(x, y);
23971         return true;
23972     },
23973
23974     /**
23975      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23976      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23977      */
23978     getProxy : function(){
23979         return this.proxy;  
23980     },
23981
23982     /**
23983      * Hides the drag source's {@link Roo.dd.StatusProxy}
23984      */
23985     hideProxy : function(){
23986         this.proxy.hide();  
23987         this.proxy.reset(true);
23988         this.dragging = false;
23989     },
23990
23991     // private
23992     triggerCacheRefresh : function(){
23993         Roo.dd.DDM.refreshCache(this.groups);
23994     },
23995
23996     // private - override to prevent hiding
23997     b4EndDrag: function(e) {
23998     },
23999
24000     // private - override to prevent moving
24001     endDrag : function(e){
24002         this.onEndDrag(this.dragData, e);
24003     },
24004
24005     // private
24006     onEndDrag : function(data, e){
24007     },
24008     
24009     // private - pin to cursor
24010     autoOffset : function(x, y) {
24011         this.setDelta(-12, -20);
24012     }    
24013 });/*
24014  * Based on:
24015  * Ext JS Library 1.1.1
24016  * Copyright(c) 2006-2007, Ext JS, LLC.
24017  *
24018  * Originally Released Under LGPL - original licence link has changed is not relivant.
24019  *
24020  * Fork - LGPL
24021  * <script type="text/javascript">
24022  */
24023
24024
24025 /**
24026  * @class Roo.dd.DropTarget
24027  * @extends Roo.dd.DDTarget
24028  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24029  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24030  * @constructor
24031  * @param {String/HTMLElement/Element} el The container element
24032  * @param {Object} config
24033  */
24034 Roo.dd.DropTarget = function(el, config){
24035     this.el = Roo.get(el);
24036     
24037     var listeners = false; ;
24038     if (config && config.listeners) {
24039         listeners= config.listeners;
24040         delete config.listeners;
24041     }
24042     Roo.apply(this, config);
24043     
24044     if(this.containerScroll){
24045         Roo.dd.ScrollManager.register(this.el);
24046     }
24047     this.addEvents( {
24048          /**
24049          * @scope Roo.dd.DropTarget
24050          */
24051          
24052          /**
24053          * @event enter
24054          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24055          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24056          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24057          * 
24058          * IMPORTANT : it should set  this.valid to true|false
24059          * 
24060          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24061          * @param {Event} e The event
24062          * @param {Object} data An object containing arbitrary data supplied by the drag source
24063          */
24064         "enter" : true,
24065         
24066          /**
24067          * @event over
24068          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24069          * This method will be called on every mouse movement while the drag source is over the drop target.
24070          * This default implementation simply returns the dropAllowed config value.
24071          * 
24072          * IMPORTANT : it should set  this.valid to true|false
24073          * 
24074          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24075          * @param {Event} e The event
24076          * @param {Object} data An object containing arbitrary data supplied by the drag source
24077          
24078          */
24079         "over" : true,
24080         /**
24081          * @event out
24082          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24083          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24084          * overClass (if any) from the drop element.
24085          * 
24086          * 
24087          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24088          * @param {Event} e The event
24089          * @param {Object} data An object containing arbitrary data supplied by the drag source
24090          */
24091          "out" : true,
24092          
24093         /**
24094          * @event drop
24095          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24096          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24097          * implementation that does something to process the drop event and returns true so that the drag source's
24098          * repair action does not run.
24099          * 
24100          * IMPORTANT : it should set this.success
24101          * 
24102          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24103          * @param {Event} e The event
24104          * @param {Object} data An object containing arbitrary data supplied by the drag source
24105         */
24106          "drop" : true
24107     });
24108             
24109      
24110     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24111         this.el.dom, 
24112         this.ddGroup || this.group,
24113         {
24114             isTarget: true,
24115             listeners : listeners || {} 
24116            
24117         
24118         }
24119     );
24120
24121 };
24122
24123 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24124     /**
24125      * @cfg {String} overClass
24126      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24127      */
24128      /**
24129      * @cfg {String} ddGroup
24130      * The drag drop group to handle drop events for
24131      */
24132      
24133     /**
24134      * @cfg {String} dropAllowed
24135      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24136      */
24137     dropAllowed : "x-dd-drop-ok",
24138     /**
24139      * @cfg {String} dropNotAllowed
24140      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24141      */
24142     dropNotAllowed : "x-dd-drop-nodrop",
24143     /**
24144      * @cfg {boolean} success
24145      * set this after drop listener.. 
24146      */
24147     success : false,
24148     /**
24149      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24150      * if the drop point is valid for over/enter..
24151      */
24152     valid : false,
24153     // private
24154     isTarget : true,
24155
24156     // private
24157     isNotifyTarget : true,
24158     
24159     /**
24160      * @hide
24161      */
24162     notifyEnter : function(dd, e, data)
24163     {
24164         this.valid = true;
24165         this.fireEvent('enter', dd, e, data);
24166         if(this.overClass){
24167             this.el.addClass(this.overClass);
24168         }
24169         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24170             this.valid ? this.dropAllowed : this.dropNotAllowed
24171         );
24172     },
24173
24174     /**
24175      * @hide
24176      */
24177     notifyOver : function(dd, e, data)
24178     {
24179         this.valid = true;
24180         this.fireEvent('over', dd, e, data);
24181         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24182             this.valid ? this.dropAllowed : this.dropNotAllowed
24183         );
24184     },
24185
24186     /**
24187      * @hide
24188      */
24189     notifyOut : function(dd, e, data)
24190     {
24191         this.fireEvent('out', dd, e, data);
24192         if(this.overClass){
24193             this.el.removeClass(this.overClass);
24194         }
24195     },
24196
24197     /**
24198      * @hide
24199      */
24200     notifyDrop : function(dd, e, data)
24201     {
24202         this.success = false;
24203         this.fireEvent('drop', dd, e, data);
24204         return this.success;
24205     }
24206 });/*
24207  * Based on:
24208  * Ext JS Library 1.1.1
24209  * Copyright(c) 2006-2007, Ext JS, LLC.
24210  *
24211  * Originally Released Under LGPL - original licence link has changed is not relivant.
24212  *
24213  * Fork - LGPL
24214  * <script type="text/javascript">
24215  */
24216
24217
24218 /**
24219  * @class Roo.dd.DragZone
24220  * @extends Roo.dd.DragSource
24221  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24222  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24223  * @constructor
24224  * @param {String/HTMLElement/Element} el The container element
24225  * @param {Object} config
24226  */
24227 Roo.dd.DragZone = function(el, config){
24228     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24229     if(this.containerScroll){
24230         Roo.dd.ScrollManager.register(this.el);
24231     }
24232 };
24233
24234 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24235     /**
24236      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24237      * for auto scrolling during drag operations.
24238      */
24239     /**
24240      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24241      * method after a failed drop (defaults to "c3daf9" - light blue)
24242      */
24243
24244     /**
24245      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24246      * for a valid target to drag based on the mouse down. Override this method
24247      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24248      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24249      * @param {EventObject} e The mouse down event
24250      * @return {Object} The dragData
24251      */
24252     getDragData : function(e){
24253         return Roo.dd.Registry.getHandleFromEvent(e);
24254     },
24255     
24256     /**
24257      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24258      * this.dragData.ddel
24259      * @param {Number} x The x position of the click on the dragged object
24260      * @param {Number} y The y position of the click on the dragged object
24261      * @return {Boolean} true to continue the drag, false to cancel
24262      */
24263     onInitDrag : function(x, y){
24264         this.proxy.update(this.dragData.ddel.cloneNode(true));
24265         this.onStartDrag(x, y);
24266         return true;
24267     },
24268     
24269     /**
24270      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24271      */
24272     afterRepair : function(){
24273         if(Roo.enableFx){
24274             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24275         }
24276         this.dragging = false;
24277     },
24278
24279     /**
24280      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24281      * the XY of this.dragData.ddel
24282      * @param {EventObject} e The mouse up event
24283      * @return {Array} The xy location (e.g. [100, 200])
24284      */
24285     getRepairXY : function(e){
24286         return Roo.Element.fly(this.dragData.ddel).getXY();  
24287     }
24288 });/*
24289  * Based on:
24290  * Ext JS Library 1.1.1
24291  * Copyright(c) 2006-2007, Ext JS, LLC.
24292  *
24293  * Originally Released Under LGPL - original licence link has changed is not relivant.
24294  *
24295  * Fork - LGPL
24296  * <script type="text/javascript">
24297  */
24298 /**
24299  * @class Roo.dd.DropZone
24300  * @extends Roo.dd.DropTarget
24301  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24302  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24303  * @constructor
24304  * @param {String/HTMLElement/Element} el The container element
24305  * @param {Object} config
24306  */
24307 Roo.dd.DropZone = function(el, config){
24308     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24309 };
24310
24311 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24312     /**
24313      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24314      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24315      * provide your own custom lookup.
24316      * @param {Event} e The event
24317      * @return {Object} data The custom data
24318      */
24319     getTargetFromEvent : function(e){
24320         return Roo.dd.Registry.getTargetFromEvent(e);
24321     },
24322
24323     /**
24324      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24325      * that it has registered.  This method has no default implementation and should be overridden to provide
24326      * node-specific processing if necessary.
24327      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24328      * {@link #getTargetFromEvent} for this node)
24329      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24330      * @param {Event} e The event
24331      * @param {Object} data An object containing arbitrary data supplied by the drag source
24332      */
24333     onNodeEnter : function(n, dd, e, data){
24334         
24335     },
24336
24337     /**
24338      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24339      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24340      * overridden to provide the proper feedback.
24341      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24342      * {@link #getTargetFromEvent} for this node)
24343      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24344      * @param {Event} e The event
24345      * @param {Object} data An object containing arbitrary data supplied by the drag source
24346      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24347      * underlying {@link Roo.dd.StatusProxy} can be updated
24348      */
24349     onNodeOver : function(n, dd, e, data){
24350         return this.dropAllowed;
24351     },
24352
24353     /**
24354      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24355      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24356      * node-specific processing if necessary.
24357      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24358      * {@link #getTargetFromEvent} for this node)
24359      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24360      * @param {Event} e The event
24361      * @param {Object} data An object containing arbitrary data supplied by the drag source
24362      */
24363     onNodeOut : function(n, dd, e, data){
24364         
24365     },
24366
24367     /**
24368      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24369      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24370      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24371      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24372      * {@link #getTargetFromEvent} for this node)
24373      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24374      * @param {Event} e The event
24375      * @param {Object} data An object containing arbitrary data supplied by the drag source
24376      * @return {Boolean} True if the drop was valid, else false
24377      */
24378     onNodeDrop : function(n, dd, e, data){
24379         return false;
24380     },
24381
24382     /**
24383      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24384      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24385      * it should be overridden to provide the proper feedback if necessary.
24386      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24387      * @param {Event} e The event
24388      * @param {Object} data An object containing arbitrary data supplied by the drag source
24389      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24390      * underlying {@link Roo.dd.StatusProxy} can be updated
24391      */
24392     onContainerOver : function(dd, e, data){
24393         return this.dropNotAllowed;
24394     },
24395
24396     /**
24397      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24398      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24399      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24400      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24401      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24402      * @param {Event} e The event
24403      * @param {Object} data An object containing arbitrary data supplied by the drag source
24404      * @return {Boolean} True if the drop was valid, else false
24405      */
24406     onContainerDrop : function(dd, e, data){
24407         return false;
24408     },
24409
24410     /**
24411      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24412      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24413      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24414      * you should override this method and provide a custom implementation.
24415      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24416      * @param {Event} e The event
24417      * @param {Object} data An object containing arbitrary data supplied by the drag source
24418      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24419      * underlying {@link Roo.dd.StatusProxy} can be updated
24420      */
24421     notifyEnter : function(dd, e, data){
24422         return this.dropNotAllowed;
24423     },
24424
24425     /**
24426      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24427      * This method will be called on every mouse movement while the drag source is over the drop zone.
24428      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24429      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24430      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24431      * registered node, it will call {@link #onContainerOver}.
24432      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24433      * @param {Event} e The event
24434      * @param {Object} data An object containing arbitrary data supplied by the drag source
24435      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24436      * underlying {@link Roo.dd.StatusProxy} can be updated
24437      */
24438     notifyOver : function(dd, e, data){
24439         var n = this.getTargetFromEvent(e);
24440         if(!n){ // not over valid drop target
24441             if(this.lastOverNode){
24442                 this.onNodeOut(this.lastOverNode, dd, e, data);
24443                 this.lastOverNode = null;
24444             }
24445             return this.onContainerOver(dd, e, data);
24446         }
24447         if(this.lastOverNode != n){
24448             if(this.lastOverNode){
24449                 this.onNodeOut(this.lastOverNode, dd, e, data);
24450             }
24451             this.onNodeEnter(n, dd, e, data);
24452             this.lastOverNode = n;
24453         }
24454         return this.onNodeOver(n, dd, e, data);
24455     },
24456
24457     /**
24458      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24459      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24460      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24461      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24462      * @param {Event} e The event
24463      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24464      */
24465     notifyOut : function(dd, e, data){
24466         if(this.lastOverNode){
24467             this.onNodeOut(this.lastOverNode, dd, e, data);
24468             this.lastOverNode = null;
24469         }
24470     },
24471
24472     /**
24473      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24474      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24475      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24476      * otherwise it will call {@link #onContainerDrop}.
24477      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24478      * @param {Event} e The event
24479      * @param {Object} data An object containing arbitrary data supplied by the drag source
24480      * @return {Boolean} True if the drop was valid, else false
24481      */
24482     notifyDrop : function(dd, e, data){
24483         if(this.lastOverNode){
24484             this.onNodeOut(this.lastOverNode, dd, e, data);
24485             this.lastOverNode = null;
24486         }
24487         var n = this.getTargetFromEvent(e);
24488         return n ?
24489             this.onNodeDrop(n, dd, e, data) :
24490             this.onContainerDrop(dd, e, data);
24491     },
24492
24493     // private
24494     triggerCacheRefresh : function(){
24495         Roo.dd.DDM.refreshCache(this.groups);
24496     }  
24497 });/*
24498  * Based on:
24499  * Ext JS Library 1.1.1
24500  * Copyright(c) 2006-2007, Ext JS, LLC.
24501  *
24502  * Originally Released Under LGPL - original licence link has changed is not relivant.
24503  *
24504  * Fork - LGPL
24505  * <script type="text/javascript">
24506  */
24507
24508
24509 /**
24510  * @class Roo.data.SortTypes
24511  * @static
24512  * Defines the default sorting (casting?) comparison functions used when sorting data.
24513  */
24514 Roo.data.SortTypes = {
24515     /**
24516      * Default sort that does nothing
24517      * @param {Mixed} s The value being converted
24518      * @return {Mixed} The comparison value
24519      */
24520     none : function(s){
24521         return s;
24522     },
24523     
24524     /**
24525      * The regular expression used to strip tags
24526      * @type {RegExp}
24527      * @property
24528      */
24529     stripTagsRE : /<\/?[^>]+>/gi,
24530     
24531     /**
24532      * Strips all HTML tags to sort on text only
24533      * @param {Mixed} s The value being converted
24534      * @return {String} The comparison value
24535      */
24536     asText : function(s){
24537         return String(s).replace(this.stripTagsRE, "");
24538     },
24539     
24540     /**
24541      * Strips all HTML tags to sort on text only - Case insensitive
24542      * @param {Mixed} s The value being converted
24543      * @return {String} The comparison value
24544      */
24545     asUCText : function(s){
24546         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24547     },
24548     
24549     /**
24550      * Case insensitive string
24551      * @param {Mixed} s The value being converted
24552      * @return {String} The comparison value
24553      */
24554     asUCString : function(s) {
24555         return String(s).toUpperCase();
24556     },
24557     
24558     /**
24559      * Date sorting
24560      * @param {Mixed} s The value being converted
24561      * @return {Number} The comparison value
24562      */
24563     asDate : function(s) {
24564         if(!s){
24565             return 0;
24566         }
24567         if(s instanceof Date){
24568             return s.getTime();
24569         }
24570         return Date.parse(String(s));
24571     },
24572     
24573     /**
24574      * Float sorting
24575      * @param {Mixed} s The value being converted
24576      * @return {Float} The comparison value
24577      */
24578     asFloat : function(s) {
24579         var val = parseFloat(String(s).replace(/,/g, ""));
24580         if(isNaN(val)) {
24581             val = 0;
24582         }
24583         return val;
24584     },
24585     
24586     /**
24587      * Integer sorting
24588      * @param {Mixed} s The value being converted
24589      * @return {Number} The comparison value
24590      */
24591     asInt : function(s) {
24592         var val = parseInt(String(s).replace(/,/g, ""));
24593         if(isNaN(val)) {
24594             val = 0;
24595         }
24596         return val;
24597     }
24598 };/*
24599  * Based on:
24600  * Ext JS Library 1.1.1
24601  * Copyright(c) 2006-2007, Ext JS, LLC.
24602  *
24603  * Originally Released Under LGPL - original licence link has changed is not relivant.
24604  *
24605  * Fork - LGPL
24606  * <script type="text/javascript">
24607  */
24608
24609 /**
24610 * @class Roo.data.Record
24611  * Instances of this class encapsulate both record <em>definition</em> information, and record
24612  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24613  * to access Records cached in an {@link Roo.data.Store} object.<br>
24614  * <p>
24615  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24616  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24617  * objects.<br>
24618  * <p>
24619  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24620  * @constructor
24621  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24622  * {@link #create}. The parameters are the same.
24623  * @param {Array} data An associative Array of data values keyed by the field name.
24624  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24625  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24626  * not specified an integer id is generated.
24627  */
24628 Roo.data.Record = function(data, id){
24629     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24630     this.data = data;
24631 };
24632
24633 /**
24634  * Generate a constructor for a specific record layout.
24635  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24636  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24637  * Each field definition object may contain the following properties: <ul>
24638  * <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,
24639  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24640  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24641  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24642  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24643  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24644  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24645  * this may be omitted.</p></li>
24646  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24647  * <ul><li>auto (Default, implies no conversion)</li>
24648  * <li>string</li>
24649  * <li>int</li>
24650  * <li>float</li>
24651  * <li>boolean</li>
24652  * <li>date</li></ul></p></li>
24653  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24654  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24655  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24656  * by the Reader into an object that will be stored in the Record. It is passed the
24657  * following parameters:<ul>
24658  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24659  * </ul></p></li>
24660  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24661  * </ul>
24662  * <br>usage:<br><pre><code>
24663 var TopicRecord = Roo.data.Record.create(
24664     {name: 'title', mapping: 'topic_title'},
24665     {name: 'author', mapping: 'username'},
24666     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24667     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24668     {name: 'lastPoster', mapping: 'user2'},
24669     {name: 'excerpt', mapping: 'post_text'}
24670 );
24671
24672 var myNewRecord = new TopicRecord({
24673     title: 'Do my job please',
24674     author: 'noobie',
24675     totalPosts: 1,
24676     lastPost: new Date(),
24677     lastPoster: 'Animal',
24678     excerpt: 'No way dude!'
24679 });
24680 myStore.add(myNewRecord);
24681 </code></pre>
24682  * @method create
24683  * @static
24684  */
24685 Roo.data.Record.create = function(o){
24686     var f = function(){
24687         f.superclass.constructor.apply(this, arguments);
24688     };
24689     Roo.extend(f, Roo.data.Record);
24690     var p = f.prototype;
24691     p.fields = new Roo.util.MixedCollection(false, function(field){
24692         return field.name;
24693     });
24694     for(var i = 0, len = o.length; i < len; i++){
24695         p.fields.add(new Roo.data.Field(o[i]));
24696     }
24697     f.getField = function(name){
24698         return p.fields.get(name);  
24699     };
24700     return f;
24701 };
24702
24703 Roo.data.Record.AUTO_ID = 1000;
24704 Roo.data.Record.EDIT = 'edit';
24705 Roo.data.Record.REJECT = 'reject';
24706 Roo.data.Record.COMMIT = 'commit';
24707
24708 Roo.data.Record.prototype = {
24709     /**
24710      * Readonly flag - true if this record has been modified.
24711      * @type Boolean
24712      */
24713     dirty : false,
24714     editing : false,
24715     error: null,
24716     modified: null,
24717
24718     // private
24719     join : function(store){
24720         this.store = store;
24721     },
24722
24723     /**
24724      * Set the named field to the specified value.
24725      * @param {String} name The name of the field to set.
24726      * @param {Object} value The value to set the field to.
24727      */
24728     set : function(name, value){
24729         if(this.data[name] == value){
24730             return;
24731         }
24732         this.dirty = true;
24733         if(!this.modified){
24734             this.modified = {};
24735         }
24736         if(typeof this.modified[name] == 'undefined'){
24737             this.modified[name] = this.data[name];
24738         }
24739         this.data[name] = value;
24740         if(!this.editing && this.store){
24741             this.store.afterEdit(this);
24742         }       
24743     },
24744
24745     /**
24746      * Get the value of the named field.
24747      * @param {String} name The name of the field to get the value of.
24748      * @return {Object} The value of the field.
24749      */
24750     get : function(name){
24751         return this.data[name]; 
24752     },
24753
24754     // private
24755     beginEdit : function(){
24756         this.editing = true;
24757         this.modified = {}; 
24758     },
24759
24760     // private
24761     cancelEdit : function(){
24762         this.editing = false;
24763         delete this.modified;
24764     },
24765
24766     // private
24767     endEdit : function(){
24768         this.editing = false;
24769         if(this.dirty && this.store){
24770             this.store.afterEdit(this);
24771         }
24772     },
24773
24774     /**
24775      * Usually called by the {@link Roo.data.Store} which owns the Record.
24776      * Rejects all changes made to the Record since either creation, or the last commit operation.
24777      * Modified fields are reverted to their original values.
24778      * <p>
24779      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24780      * of reject operations.
24781      */
24782     reject : function(){
24783         var m = this.modified;
24784         for(var n in m){
24785             if(typeof m[n] != "function"){
24786                 this.data[n] = m[n];
24787             }
24788         }
24789         this.dirty = false;
24790         delete this.modified;
24791         this.editing = false;
24792         if(this.store){
24793             this.store.afterReject(this);
24794         }
24795     },
24796
24797     /**
24798      * Usually called by the {@link Roo.data.Store} which owns the Record.
24799      * Commits all changes made to the Record since either creation, or the last commit operation.
24800      * <p>
24801      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24802      * of commit operations.
24803      */
24804     commit : function(){
24805         this.dirty = false;
24806         delete this.modified;
24807         this.editing = false;
24808         if(this.store){
24809             this.store.afterCommit(this);
24810         }
24811     },
24812
24813     // private
24814     hasError : function(){
24815         return this.error != null;
24816     },
24817
24818     // private
24819     clearError : function(){
24820         this.error = null;
24821     },
24822
24823     /**
24824      * Creates a copy of this record.
24825      * @param {String} id (optional) A new record id if you don't want to use this record's id
24826      * @return {Record}
24827      */
24828     copy : function(newId) {
24829         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24830     }
24831 };/*
24832  * Based on:
24833  * Ext JS Library 1.1.1
24834  * Copyright(c) 2006-2007, Ext JS, LLC.
24835  *
24836  * Originally Released Under LGPL - original licence link has changed is not relivant.
24837  *
24838  * Fork - LGPL
24839  * <script type="text/javascript">
24840  */
24841
24842
24843
24844 /**
24845  * @class Roo.data.Store
24846  * @extends Roo.util.Observable
24847  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24848  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24849  * <p>
24850  * 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
24851  * has no knowledge of the format of the data returned by the Proxy.<br>
24852  * <p>
24853  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24854  * instances from the data object. These records are cached and made available through accessor functions.
24855  * @constructor
24856  * Creates a new Store.
24857  * @param {Object} config A config object containing the objects needed for the Store to access data,
24858  * and read the data into Records.
24859  */
24860 Roo.data.Store = function(config){
24861     this.data = new Roo.util.MixedCollection(false);
24862     this.data.getKey = function(o){
24863         return o.id;
24864     };
24865     this.baseParams = {};
24866     // private
24867     this.paramNames = {
24868         "start" : "start",
24869         "limit" : "limit",
24870         "sort" : "sort",
24871         "dir" : "dir",
24872         "multisort" : "_multisort"
24873     };
24874
24875     if(config && config.data){
24876         this.inlineData = config.data;
24877         delete config.data;
24878     }
24879
24880     Roo.apply(this, config);
24881     
24882     if(this.reader){ // reader passed
24883         this.reader = Roo.factory(this.reader, Roo.data);
24884         this.reader.xmodule = this.xmodule || false;
24885         if(!this.recordType){
24886             this.recordType = this.reader.recordType;
24887         }
24888         if(this.reader.onMetaChange){
24889             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24890         }
24891     }
24892
24893     if(this.recordType){
24894         this.fields = this.recordType.prototype.fields;
24895     }
24896     this.modified = [];
24897
24898     this.addEvents({
24899         /**
24900          * @event datachanged
24901          * Fires when the data cache has changed, and a widget which is using this Store
24902          * as a Record cache should refresh its view.
24903          * @param {Store} this
24904          */
24905         datachanged : true,
24906         /**
24907          * @event metachange
24908          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24909          * @param {Store} this
24910          * @param {Object} meta The JSON metadata
24911          */
24912         metachange : true,
24913         /**
24914          * @event add
24915          * Fires when Records have been added to the Store
24916          * @param {Store} this
24917          * @param {Roo.data.Record[]} records The array of Records added
24918          * @param {Number} index The index at which the record(s) were added
24919          */
24920         add : true,
24921         /**
24922          * @event remove
24923          * Fires when a Record has been removed from the Store
24924          * @param {Store} this
24925          * @param {Roo.data.Record} record The Record that was removed
24926          * @param {Number} index The index at which the record was removed
24927          */
24928         remove : true,
24929         /**
24930          * @event update
24931          * Fires when a Record has been updated
24932          * @param {Store} this
24933          * @param {Roo.data.Record} record The Record that was updated
24934          * @param {String} operation The update operation being performed.  Value may be one of:
24935          * <pre><code>
24936  Roo.data.Record.EDIT
24937  Roo.data.Record.REJECT
24938  Roo.data.Record.COMMIT
24939          * </code></pre>
24940          */
24941         update : true,
24942         /**
24943          * @event clear
24944          * Fires when the data cache has been cleared.
24945          * @param {Store} this
24946          */
24947         clear : true,
24948         /**
24949          * @event beforeload
24950          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24951          * the load action will be canceled.
24952          * @param {Store} this
24953          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24954          */
24955         beforeload : true,
24956         /**
24957          * @event beforeloadadd
24958          * Fires after a new set of Records has been loaded.
24959          * @param {Store} this
24960          * @param {Roo.data.Record[]} records The Records that were loaded
24961          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24962          */
24963         beforeloadadd : true,
24964         /**
24965          * @event load
24966          * Fires after a new set of Records has been loaded, before they are added to the store.
24967          * @param {Store} this
24968          * @param {Roo.data.Record[]} records The Records that were loaded
24969          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24970          * @params {Object} return from reader
24971          */
24972         load : true,
24973         /**
24974          * @event loadexception
24975          * Fires if an exception occurs in the Proxy during loading.
24976          * Called with the signature of the Proxy's "loadexception" event.
24977          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24978          * 
24979          * @param {Proxy} 
24980          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24981          * @param {Object} load options 
24982          * @param {Object} jsonData from your request (normally this contains the Exception)
24983          */
24984         loadexception : true
24985     });
24986     
24987     if(this.proxy){
24988         this.proxy = Roo.factory(this.proxy, Roo.data);
24989         this.proxy.xmodule = this.xmodule || false;
24990         this.relayEvents(this.proxy,  ["loadexception"]);
24991     }
24992     this.sortToggle = {};
24993     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24994
24995     Roo.data.Store.superclass.constructor.call(this);
24996
24997     if(this.inlineData){
24998         this.loadData(this.inlineData);
24999         delete this.inlineData;
25000     }
25001 };
25002
25003 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25004      /**
25005     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
25006     * without a remote query - used by combo/forms at present.
25007     */
25008     
25009     /**
25010     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25011     */
25012     /**
25013     * @cfg {Array} data Inline data to be loaded when the store is initialized.
25014     */
25015     /**
25016     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
25017     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25018     */
25019     /**
25020     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25021     * on any HTTP request
25022     */
25023     /**
25024     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25025     */
25026     /**
25027     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25028     */
25029     multiSort: false,
25030     /**
25031     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25032     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25033     */
25034     remoteSort : false,
25035
25036     /**
25037     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25038      * loaded or when a record is removed. (defaults to false).
25039     */
25040     pruneModifiedRecords : false,
25041
25042     // private
25043     lastOptions : null,
25044
25045     /**
25046      * Add Records to the Store and fires the add event.
25047      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25048      */
25049     add : function(records){
25050         records = [].concat(records);
25051         for(var i = 0, len = records.length; i < len; i++){
25052             records[i].join(this);
25053         }
25054         var index = this.data.length;
25055         this.data.addAll(records);
25056         this.fireEvent("add", this, records, index);
25057     },
25058
25059     /**
25060      * Remove a Record from the Store and fires the remove event.
25061      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25062      */
25063     remove : function(record){
25064         var index = this.data.indexOf(record);
25065         this.data.removeAt(index);
25066  
25067         if(this.pruneModifiedRecords){
25068             this.modified.remove(record);
25069         }
25070         this.fireEvent("remove", this, record, index);
25071     },
25072
25073     /**
25074      * Remove all Records from the Store and fires the clear event.
25075      */
25076     removeAll : function(){
25077         this.data.clear();
25078         if(this.pruneModifiedRecords){
25079             this.modified = [];
25080         }
25081         this.fireEvent("clear", this);
25082     },
25083
25084     /**
25085      * Inserts Records to the Store at the given index and fires the add event.
25086      * @param {Number} index The start index at which to insert the passed Records.
25087      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25088      */
25089     insert : function(index, records){
25090         records = [].concat(records);
25091         for(var i = 0, len = records.length; i < len; i++){
25092             this.data.insert(index, records[i]);
25093             records[i].join(this);
25094         }
25095         this.fireEvent("add", this, records, index);
25096     },
25097
25098     /**
25099      * Get the index within the cache of the passed Record.
25100      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25101      * @return {Number} The index of the passed Record. Returns -1 if not found.
25102      */
25103     indexOf : function(record){
25104         return this.data.indexOf(record);
25105     },
25106
25107     /**
25108      * Get the index within the cache of the Record with the passed id.
25109      * @param {String} id The id of the Record to find.
25110      * @return {Number} The index of the Record. Returns -1 if not found.
25111      */
25112     indexOfId : function(id){
25113         return this.data.indexOfKey(id);
25114     },
25115
25116     /**
25117      * Get the Record with the specified id.
25118      * @param {String} id The id of the Record to find.
25119      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25120      */
25121     getById : function(id){
25122         return this.data.key(id);
25123     },
25124
25125     /**
25126      * Get the Record at the specified index.
25127      * @param {Number} index The index of the Record to find.
25128      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25129      */
25130     getAt : function(index){
25131         return this.data.itemAt(index);
25132     },
25133
25134     /**
25135      * Returns a range of Records between specified indices.
25136      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25137      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25138      * @return {Roo.data.Record[]} An array of Records
25139      */
25140     getRange : function(start, end){
25141         return this.data.getRange(start, end);
25142     },
25143
25144     // private
25145     storeOptions : function(o){
25146         o = Roo.apply({}, o);
25147         delete o.callback;
25148         delete o.scope;
25149         this.lastOptions = o;
25150     },
25151
25152     /**
25153      * Loads the Record cache from the configured Proxy using the configured Reader.
25154      * <p>
25155      * If using remote paging, then the first load call must specify the <em>start</em>
25156      * and <em>limit</em> properties in the options.params property to establish the initial
25157      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25158      * <p>
25159      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25160      * and this call will return before the new data has been loaded. Perform any post-processing
25161      * in a callback function, or in a "load" event handler.</strong>
25162      * <p>
25163      * @param {Object} options An object containing properties which control loading options:<ul>
25164      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25165      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25166      * <pre>
25167                 {
25168                     data : data,  // array of key=>value data like JsonReader
25169                     total : data.length,
25170                     success : true
25171                     
25172                 }
25173         </pre>
25174             }.</li>
25175      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25176      * passed the following arguments:<ul>
25177      * <li>r : Roo.data.Record[]</li>
25178      * <li>options: Options object from the load call</li>
25179      * <li>success: Boolean success indicator</li></ul></li>
25180      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25181      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25182      * </ul>
25183      */
25184     load : function(options){
25185         options = options || {};
25186         if(this.fireEvent("beforeload", this, options) !== false){
25187             this.storeOptions(options);
25188             var p = Roo.apply(options.params || {}, this.baseParams);
25189             // if meta was not loaded from remote source.. try requesting it.
25190             if (!this.reader.metaFromRemote) {
25191                 p._requestMeta = 1;
25192             }
25193             if(this.sortInfo && this.remoteSort){
25194                 var pn = this.paramNames;
25195                 p[pn["sort"]] = this.sortInfo.field;
25196                 p[pn["dir"]] = this.sortInfo.direction;
25197             }
25198             if (this.multiSort) {
25199                 var pn = this.paramNames;
25200                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25201             }
25202             
25203             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25204         }
25205     },
25206
25207     /**
25208      * Reloads the Record cache from the configured Proxy using the configured Reader and
25209      * the options from the last load operation performed.
25210      * @param {Object} options (optional) An object containing properties which may override the options
25211      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25212      * the most recently used options are reused).
25213      */
25214     reload : function(options){
25215         this.load(Roo.applyIf(options||{}, this.lastOptions));
25216     },
25217
25218     // private
25219     // Called as a callback by the Reader during a load operation.
25220     loadRecords : function(o, options, success){
25221          
25222         if(!o){
25223             if(success !== false){
25224                 this.fireEvent("load", this, [], options, o);
25225             }
25226             if(options.callback){
25227                 options.callback.call(options.scope || this, [], options, false);
25228             }
25229             return;
25230         }
25231         // if data returned failure - throw an exception.
25232         if (o.success === false) {
25233             // show a message if no listener is registered.
25234             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25235                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25236             }
25237             // loadmask wil be hooked into this..
25238             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25239             return;
25240         }
25241         var r = o.records, t = o.totalRecords || r.length;
25242         
25243         this.fireEvent("beforeloadadd", this, r, options, o);
25244         
25245         if(!options || options.add !== true){
25246             if(this.pruneModifiedRecords){
25247                 this.modified = [];
25248             }
25249             for(var i = 0, len = r.length; i < len; i++){
25250                 r[i].join(this);
25251             }
25252             if(this.snapshot){
25253                 this.data = this.snapshot;
25254                 delete this.snapshot;
25255             }
25256             this.data.clear();
25257             this.data.addAll(r);
25258             this.totalLength = t;
25259             this.applySort();
25260             this.fireEvent("datachanged", this);
25261         }else{
25262             this.totalLength = Math.max(t, this.data.length+r.length);
25263             this.add(r);
25264         }
25265         
25266         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25267                 
25268             var e = new Roo.data.Record({});
25269
25270             e.set(this.parent.displayField, this.parent.emptyTitle);
25271             e.set(this.parent.valueField, '');
25272
25273             this.insert(0, e);
25274         }
25275             
25276         this.fireEvent("load", this, r, options, o);
25277         if(options.callback){
25278             options.callback.call(options.scope || this, r, options, true);
25279         }
25280     },
25281
25282
25283     /**
25284      * Loads data from a passed data block. A Reader which understands the format of the data
25285      * must have been configured in the constructor.
25286      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25287      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25288      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25289      */
25290     loadData : function(o, append){
25291         var r = this.reader.readRecords(o);
25292         this.loadRecords(r, {add: append}, true);
25293     },
25294     
25295      /**
25296      * using 'cn' the nested child reader read the child array into it's child stores.
25297      * @param {Object} rec The record with a 'children array
25298      */
25299     loadDataFromChildren : function(rec)
25300     {
25301         this.loadData(this.reader.toLoadData(rec));
25302     },
25303     
25304
25305     /**
25306      * Gets the number of cached records.
25307      * <p>
25308      * <em>If using paging, this may not be the total size of the dataset. If the data object
25309      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25310      * the data set size</em>
25311      */
25312     getCount : function(){
25313         return this.data.length || 0;
25314     },
25315
25316     /**
25317      * Gets the total number of records in the dataset as returned by the server.
25318      * <p>
25319      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25320      * the dataset size</em>
25321      */
25322     getTotalCount : function(){
25323         return this.totalLength || 0;
25324     },
25325
25326     /**
25327      * Returns the sort state of the Store as an object with two properties:
25328      * <pre><code>
25329  field {String} The name of the field by which the Records are sorted
25330  direction {String} The sort order, "ASC" or "DESC"
25331      * </code></pre>
25332      */
25333     getSortState : function(){
25334         return this.sortInfo;
25335     },
25336
25337     // private
25338     applySort : function(){
25339         if(this.sortInfo && !this.remoteSort){
25340             var s = this.sortInfo, f = s.field;
25341             var st = this.fields.get(f).sortType;
25342             var fn = function(r1, r2){
25343                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25344                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25345             };
25346             this.data.sort(s.direction, fn);
25347             if(this.snapshot && this.snapshot != this.data){
25348                 this.snapshot.sort(s.direction, fn);
25349             }
25350         }
25351     },
25352
25353     /**
25354      * Sets the default sort column and order to be used by the next load operation.
25355      * @param {String} fieldName The name of the field to sort by.
25356      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25357      */
25358     setDefaultSort : function(field, dir){
25359         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25360     },
25361
25362     /**
25363      * Sort the Records.
25364      * If remote sorting is used, the sort is performed on the server, and the cache is
25365      * reloaded. If local sorting is used, the cache is sorted internally.
25366      * @param {String} fieldName The name of the field to sort by.
25367      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25368      */
25369     sort : function(fieldName, dir){
25370         var f = this.fields.get(fieldName);
25371         if(!dir){
25372             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25373             
25374             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25375                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25376             }else{
25377                 dir = f.sortDir;
25378             }
25379         }
25380         this.sortToggle[f.name] = dir;
25381         this.sortInfo = {field: f.name, direction: dir};
25382         if(!this.remoteSort){
25383             this.applySort();
25384             this.fireEvent("datachanged", this);
25385         }else{
25386             this.load(this.lastOptions);
25387         }
25388     },
25389
25390     /**
25391      * Calls the specified function for each of the Records in the cache.
25392      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25393      * Returning <em>false</em> aborts and exits the iteration.
25394      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25395      */
25396     each : function(fn, scope){
25397         this.data.each(fn, scope);
25398     },
25399
25400     /**
25401      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25402      * (e.g., during paging).
25403      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25404      */
25405     getModifiedRecords : function(){
25406         return this.modified;
25407     },
25408
25409     // private
25410     createFilterFn : function(property, value, anyMatch){
25411         if(!value.exec){ // not a regex
25412             value = String(value);
25413             if(value.length == 0){
25414                 return false;
25415             }
25416             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25417         }
25418         return function(r){
25419             return value.test(r.data[property]);
25420         };
25421     },
25422
25423     /**
25424      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25425      * @param {String} property A field on your records
25426      * @param {Number} start The record index to start at (defaults to 0)
25427      * @param {Number} end The last record index to include (defaults to length - 1)
25428      * @return {Number} The sum
25429      */
25430     sum : function(property, start, end){
25431         var rs = this.data.items, v = 0;
25432         start = start || 0;
25433         end = (end || end === 0) ? end : rs.length-1;
25434
25435         for(var i = start; i <= end; i++){
25436             v += (rs[i].data[property] || 0);
25437         }
25438         return v;
25439     },
25440
25441     /**
25442      * Filter the records by a specified property.
25443      * @param {String} field A field on your records
25444      * @param {String/RegExp} value Either a string that the field
25445      * should start with or a RegExp to test against the field
25446      * @param {Boolean} anyMatch True to match any part not just the beginning
25447      */
25448     filter : function(property, value, anyMatch){
25449         var fn = this.createFilterFn(property, value, anyMatch);
25450         return fn ? this.filterBy(fn) : this.clearFilter();
25451     },
25452
25453     /**
25454      * Filter by a function. The specified function will be called with each
25455      * record in this data source. If the function returns true the record is included,
25456      * otherwise it is filtered.
25457      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25458      * @param {Object} scope (optional) The scope of the function (defaults to this)
25459      */
25460     filterBy : function(fn, scope){
25461         this.snapshot = this.snapshot || this.data;
25462         this.data = this.queryBy(fn, scope||this);
25463         this.fireEvent("datachanged", this);
25464     },
25465
25466     /**
25467      * Query the records by a specified property.
25468      * @param {String} field A field on your records
25469      * @param {String/RegExp} value Either a string that the field
25470      * should start with or a RegExp to test against the field
25471      * @param {Boolean} anyMatch True to match any part not just the beginning
25472      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25473      */
25474     query : function(property, value, anyMatch){
25475         var fn = this.createFilterFn(property, value, anyMatch);
25476         return fn ? this.queryBy(fn) : this.data.clone();
25477     },
25478
25479     /**
25480      * Query by a function. The specified function will be called with each
25481      * record in this data source. If the function returns true the record is included
25482      * in the results.
25483      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25484      * @param {Object} scope (optional) The scope of the function (defaults to this)
25485       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25486      **/
25487     queryBy : function(fn, scope){
25488         var data = this.snapshot || this.data;
25489         return data.filterBy(fn, scope||this);
25490     },
25491
25492     /**
25493      * Collects unique values for a particular dataIndex from this store.
25494      * @param {String} dataIndex The property to collect
25495      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25496      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25497      * @return {Array} An array of the unique values
25498      **/
25499     collect : function(dataIndex, allowNull, bypassFilter){
25500         var d = (bypassFilter === true && this.snapshot) ?
25501                 this.snapshot.items : this.data.items;
25502         var v, sv, r = [], l = {};
25503         for(var i = 0, len = d.length; i < len; i++){
25504             v = d[i].data[dataIndex];
25505             sv = String(v);
25506             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25507                 l[sv] = true;
25508                 r[r.length] = v;
25509             }
25510         }
25511         return r;
25512     },
25513
25514     /**
25515      * Revert to a view of the Record cache with no filtering applied.
25516      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25517      */
25518     clearFilter : function(suppressEvent){
25519         if(this.snapshot && this.snapshot != this.data){
25520             this.data = this.snapshot;
25521             delete this.snapshot;
25522             if(suppressEvent !== true){
25523                 this.fireEvent("datachanged", this);
25524             }
25525         }
25526     },
25527
25528     // private
25529     afterEdit : function(record){
25530         if(this.modified.indexOf(record) == -1){
25531             this.modified.push(record);
25532         }
25533         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25534     },
25535     
25536     // private
25537     afterReject : function(record){
25538         this.modified.remove(record);
25539         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25540     },
25541
25542     // private
25543     afterCommit : function(record){
25544         this.modified.remove(record);
25545         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25546     },
25547
25548     /**
25549      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25550      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25551      */
25552     commitChanges : function(){
25553         var m = this.modified.slice(0);
25554         this.modified = [];
25555         for(var i = 0, len = m.length; i < len; i++){
25556             m[i].commit();
25557         }
25558     },
25559
25560     /**
25561      * Cancel outstanding changes on all changed records.
25562      */
25563     rejectChanges : function(){
25564         var m = this.modified.slice(0);
25565         this.modified = [];
25566         for(var i = 0, len = m.length; i < len; i++){
25567             m[i].reject();
25568         }
25569     },
25570
25571     onMetaChange : function(meta, rtype, o){
25572         this.recordType = rtype;
25573         this.fields = rtype.prototype.fields;
25574         delete this.snapshot;
25575         this.sortInfo = meta.sortInfo || this.sortInfo;
25576         this.modified = [];
25577         this.fireEvent('metachange', this, this.reader.meta);
25578     },
25579     
25580     moveIndex : function(data, type)
25581     {
25582         var index = this.indexOf(data);
25583         
25584         var newIndex = index + type;
25585         
25586         this.remove(data);
25587         
25588         this.insert(newIndex, data);
25589         
25590     }
25591 });/*
25592  * Based on:
25593  * Ext JS Library 1.1.1
25594  * Copyright(c) 2006-2007, Ext JS, LLC.
25595  *
25596  * Originally Released Under LGPL - original licence link has changed is not relivant.
25597  *
25598  * Fork - LGPL
25599  * <script type="text/javascript">
25600  */
25601
25602 /**
25603  * @class Roo.data.SimpleStore
25604  * @extends Roo.data.Store
25605  * Small helper class to make creating Stores from Array data easier.
25606  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25607  * @cfg {Array} fields An array of field definition objects, or field name strings.
25608  * @cfg {Object} an existing reader (eg. copied from another store)
25609  * @cfg {Array} data The multi-dimensional array of data
25610  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25611  * @cfg {Roo.data.Reader} reader  [not-required] 
25612  * @constructor
25613  * @param {Object} config
25614  */
25615 Roo.data.SimpleStore = function(config)
25616 {
25617     Roo.data.SimpleStore.superclass.constructor.call(this, {
25618         isLocal : true,
25619         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25620                 id: config.id
25621             },
25622             Roo.data.Record.create(config.fields)
25623         ),
25624         proxy : new Roo.data.MemoryProxy(config.data)
25625     });
25626     this.load();
25627 };
25628 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25629  * Based on:
25630  * Ext JS Library 1.1.1
25631  * Copyright(c) 2006-2007, Ext JS, LLC.
25632  *
25633  * Originally Released Under LGPL - original licence link has changed is not relivant.
25634  *
25635  * Fork - LGPL
25636  * <script type="text/javascript">
25637  */
25638
25639 /**
25640 /**
25641  * @extends Roo.data.Store
25642  * @class Roo.data.JsonStore
25643  * Small helper class to make creating Stores for JSON data easier. <br/>
25644 <pre><code>
25645 var store = new Roo.data.JsonStore({
25646     url: 'get-images.php',
25647     root: 'images',
25648     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25649 });
25650 </code></pre>
25651  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25652  * JsonReader and HttpProxy (unless inline data is provided).</b>
25653  * @cfg {Array} fields An array of field definition objects, or field name strings.
25654  * @constructor
25655  * @param {Object} config
25656  */
25657 Roo.data.JsonStore = function(c){
25658     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25659         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25660         reader: new Roo.data.JsonReader(c, c.fields)
25661     }));
25662 };
25663 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25664  * Based on:
25665  * Ext JS Library 1.1.1
25666  * Copyright(c) 2006-2007, Ext JS, LLC.
25667  *
25668  * Originally Released Under LGPL - original licence link has changed is not relivant.
25669  *
25670  * Fork - LGPL
25671  * <script type="text/javascript">
25672  */
25673
25674  
25675 Roo.data.Field = function(config){
25676     if(typeof config == "string"){
25677         config = {name: config};
25678     }
25679     Roo.apply(this, config);
25680     
25681     if(!this.type){
25682         this.type = "auto";
25683     }
25684     
25685     var st = Roo.data.SortTypes;
25686     // named sortTypes are supported, here we look them up
25687     if(typeof this.sortType == "string"){
25688         this.sortType = st[this.sortType];
25689     }
25690     
25691     // set default sortType for strings and dates
25692     if(!this.sortType){
25693         switch(this.type){
25694             case "string":
25695                 this.sortType = st.asUCString;
25696                 break;
25697             case "date":
25698                 this.sortType = st.asDate;
25699                 break;
25700             default:
25701                 this.sortType = st.none;
25702         }
25703     }
25704
25705     // define once
25706     var stripRe = /[\$,%]/g;
25707
25708     // prebuilt conversion function for this field, instead of
25709     // switching every time we're reading a value
25710     if(!this.convert){
25711         var cv, dateFormat = this.dateFormat;
25712         switch(this.type){
25713             case "":
25714             case "auto":
25715             case undefined:
25716                 cv = function(v){ return v; };
25717                 break;
25718             case "string":
25719                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25720                 break;
25721             case "int":
25722                 cv = function(v){
25723                     return v !== undefined && v !== null && v !== '' ?
25724                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25725                     };
25726                 break;
25727             case "float":
25728                 cv = function(v){
25729                     return v !== undefined && v !== null && v !== '' ?
25730                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25731                     };
25732                 break;
25733             case "bool":
25734             case "boolean":
25735                 cv = function(v){ return v === true || v === "true" || v == 1; };
25736                 break;
25737             case "date":
25738                 cv = function(v){
25739                     if(!v){
25740                         return '';
25741                     }
25742                     if(v instanceof Date){
25743                         return v;
25744                     }
25745                     if(dateFormat){
25746                         if(dateFormat == "timestamp"){
25747                             return new Date(v*1000);
25748                         }
25749                         return Date.parseDate(v, dateFormat);
25750                     }
25751                     var parsed = Date.parse(v);
25752                     return parsed ? new Date(parsed) : null;
25753                 };
25754              break;
25755             
25756         }
25757         this.convert = cv;
25758     }
25759 };
25760
25761 Roo.data.Field.prototype = {
25762     dateFormat: null,
25763     defaultValue: "",
25764     mapping: null,
25765     sortType : null,
25766     sortDir : "ASC"
25767 };/*
25768  * Based on:
25769  * Ext JS Library 1.1.1
25770  * Copyright(c) 2006-2007, Ext JS, LLC.
25771  *
25772  * Originally Released Under LGPL - original licence link has changed is not relivant.
25773  *
25774  * Fork - LGPL
25775  * <script type="text/javascript">
25776  */
25777  
25778 // Base class for reading structured data from a data source.  This class is intended to be
25779 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25780
25781 /**
25782  * @class Roo.data.DataReader
25783  * @abstract
25784  * Base class for reading structured data from a data source.  This class is intended to be
25785  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25786  */
25787
25788 Roo.data.DataReader = function(meta, recordType){
25789     
25790     this.meta = meta;
25791     
25792     this.recordType = recordType instanceof Array ? 
25793         Roo.data.Record.create(recordType) : recordType;
25794 };
25795
25796 Roo.data.DataReader.prototype = {
25797     
25798     
25799     readerType : 'Data',
25800      /**
25801      * Create an empty record
25802      * @param {Object} data (optional) - overlay some values
25803      * @return {Roo.data.Record} record created.
25804      */
25805     newRow :  function(d) {
25806         var da =  {};
25807         this.recordType.prototype.fields.each(function(c) {
25808             switch( c.type) {
25809                 case 'int' : da[c.name] = 0; break;
25810                 case 'date' : da[c.name] = new Date(); break;
25811                 case 'float' : da[c.name] = 0.0; break;
25812                 case 'boolean' : da[c.name] = false; break;
25813                 default : da[c.name] = ""; break;
25814             }
25815             
25816         });
25817         return new this.recordType(Roo.apply(da, d));
25818     }
25819     
25820     
25821 };/*
25822  * Based on:
25823  * Ext JS Library 1.1.1
25824  * Copyright(c) 2006-2007, Ext JS, LLC.
25825  *
25826  * Originally Released Under LGPL - original licence link has changed is not relivant.
25827  *
25828  * Fork - LGPL
25829  * <script type="text/javascript">
25830  */
25831
25832 /**
25833  * @class Roo.data.DataProxy
25834  * @extends Roo.util.Observable
25835  * @abstract
25836  * This class is an abstract base class for implementations which provide retrieval of
25837  * unformatted data objects.<br>
25838  * <p>
25839  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25840  * (of the appropriate type which knows how to parse the data object) to provide a block of
25841  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25842  * <p>
25843  * Custom implementations must implement the load method as described in
25844  * {@link Roo.data.HttpProxy#load}.
25845  */
25846 Roo.data.DataProxy = function(){
25847     this.addEvents({
25848         /**
25849          * @event beforeload
25850          * Fires before a network request is made to retrieve a data object.
25851          * @param {Object} This DataProxy object.
25852          * @param {Object} params The params parameter to the load function.
25853          */
25854         beforeload : true,
25855         /**
25856          * @event load
25857          * Fires before the load method's callback is called.
25858          * @param {Object} This DataProxy object.
25859          * @param {Object} o The data object.
25860          * @param {Object} arg The callback argument object passed to the load function.
25861          */
25862         load : true,
25863         /**
25864          * @event loadexception
25865          * Fires if an Exception occurs during data retrieval.
25866          * @param {Object} This DataProxy object.
25867          * @param {Object} o The data object.
25868          * @param {Object} arg The callback argument object passed to the load function.
25869          * @param {Object} e The Exception.
25870          */
25871         loadexception : true
25872     });
25873     Roo.data.DataProxy.superclass.constructor.call(this);
25874 };
25875
25876 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25877
25878     /**
25879      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25880      */
25881 /*
25882  * Based on:
25883  * Ext JS Library 1.1.1
25884  * Copyright(c) 2006-2007, Ext JS, LLC.
25885  *
25886  * Originally Released Under LGPL - original licence link has changed is not relivant.
25887  *
25888  * Fork - LGPL
25889  * <script type="text/javascript">
25890  */
25891 /**
25892  * @class Roo.data.MemoryProxy
25893  * @extends Roo.data.DataProxy
25894  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25895  * to the Reader when its load method is called.
25896  * @constructor
25897  * @param {Object} config  A config object containing the objects needed for the Store to access data,
25898  */
25899 Roo.data.MemoryProxy = function(config){
25900     var data = config;
25901     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25902         data = config.data;
25903     }
25904     Roo.data.MemoryProxy.superclass.constructor.call(this);
25905     this.data = data;
25906 };
25907
25908 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25909     
25910     /**
25911      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25912      */
25913     /**
25914      * Load data from the requested source (in this case an in-memory
25915      * data object passed to the constructor), read the data object into
25916      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25917      * process that block using the passed callback.
25918      * @param {Object} params This parameter is not used by the MemoryProxy class.
25919      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25920      * object into a block of Roo.data.Records.
25921      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25922      * The function must be passed <ul>
25923      * <li>The Record block object</li>
25924      * <li>The "arg" argument from the load function</li>
25925      * <li>A boolean success indicator</li>
25926      * </ul>
25927      * @param {Object} scope The scope in which to call the callback
25928      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25929      */
25930     load : function(params, reader, callback, scope, arg){
25931         params = params || {};
25932         var result;
25933         try {
25934             result = reader.readRecords(params.data ? params.data :this.data);
25935         }catch(e){
25936             this.fireEvent("loadexception", this, arg, null, e);
25937             callback.call(scope, null, arg, false);
25938             return;
25939         }
25940         callback.call(scope, result, arg, true);
25941     },
25942     
25943     // private
25944     update : function(params, records){
25945         
25946     }
25947 });/*
25948  * Based on:
25949  * Ext JS Library 1.1.1
25950  * Copyright(c) 2006-2007, Ext JS, LLC.
25951  *
25952  * Originally Released Under LGPL - original licence link has changed is not relivant.
25953  *
25954  * Fork - LGPL
25955  * <script type="text/javascript">
25956  */
25957 /**
25958  * @class Roo.data.HttpProxy
25959  * @extends Roo.data.DataProxy
25960  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25961  * configured to reference a certain URL.<br><br>
25962  * <p>
25963  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25964  * from which the running page was served.<br><br>
25965  * <p>
25966  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25967  * <p>
25968  * Be aware that to enable the browser to parse an XML document, the server must set
25969  * the Content-Type header in the HTTP response to "text/xml".
25970  * @constructor
25971  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25972  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25973  * will be used to make the request.
25974  */
25975 Roo.data.HttpProxy = function(conn){
25976     Roo.data.HttpProxy.superclass.constructor.call(this);
25977     // is conn a conn config or a real conn?
25978     this.conn = conn;
25979     this.useAjax = !conn || !conn.events;
25980   
25981 };
25982
25983 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25984     // thse are take from connection...
25985     
25986     /**
25987      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25988      */
25989     /**
25990      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25991      * extra parameters to each request made by this object. (defaults to undefined)
25992      */
25993     /**
25994      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25995      *  to each request made by this object. (defaults to undefined)
25996      */
25997     /**
25998      * @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)
25999      */
26000     /**
26001      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
26002      */
26003      /**
26004      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
26005      * @type Boolean
26006      */
26007   
26008
26009     /**
26010      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26011      * @type Boolean
26012      */
26013     /**
26014      * Return the {@link Roo.data.Connection} object being used by this Proxy.
26015      * @return {Connection} The Connection object. This object may be used to subscribe to events on
26016      * a finer-grained basis than the DataProxy events.
26017      */
26018     getConnection : function(){
26019         return this.useAjax ? Roo.Ajax : this.conn;
26020     },
26021
26022     /**
26023      * Load data from the configured {@link Roo.data.Connection}, read the data object into
26024      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26025      * process that block using the passed callback.
26026      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26027      * for the request to the remote server.
26028      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26029      * object into a block of Roo.data.Records.
26030      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26031      * The function must be passed <ul>
26032      * <li>The Record block object</li>
26033      * <li>The "arg" argument from the load function</li>
26034      * <li>A boolean success indicator</li>
26035      * </ul>
26036      * @param {Object} scope The scope in which to call the callback
26037      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26038      */
26039     load : function(params, reader, callback, scope, arg){
26040         if(this.fireEvent("beforeload", this, params) !== false){
26041             var  o = {
26042                 params : params || {},
26043                 request: {
26044                     callback : callback,
26045                     scope : scope,
26046                     arg : arg
26047                 },
26048                 reader: reader,
26049                 callback : this.loadResponse,
26050                 scope: this
26051             };
26052             if(this.useAjax){
26053                 Roo.applyIf(o, this.conn);
26054                 if(this.activeRequest){
26055                     Roo.Ajax.abort(this.activeRequest);
26056                 }
26057                 this.activeRequest = Roo.Ajax.request(o);
26058             }else{
26059                 this.conn.request(o);
26060             }
26061         }else{
26062             callback.call(scope||this, null, arg, false);
26063         }
26064     },
26065
26066     // private
26067     loadResponse : function(o, success, response){
26068         delete this.activeRequest;
26069         if(!success){
26070             this.fireEvent("loadexception", this, o, response);
26071             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26072             return;
26073         }
26074         var result;
26075         try {
26076             result = o.reader.read(response);
26077         }catch(e){
26078             o.success = false;
26079             o.raw = { errorMsg : response.responseText };
26080             this.fireEvent("loadexception", this, o, response, e);
26081             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26082             return;
26083         }
26084         
26085         this.fireEvent("load", this, o, o.request.arg);
26086         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26087     },
26088
26089     // private
26090     update : function(dataSet){
26091
26092     },
26093
26094     // private
26095     updateResponse : function(dataSet){
26096
26097     }
26098 });/*
26099  * Based on:
26100  * Ext JS Library 1.1.1
26101  * Copyright(c) 2006-2007, Ext JS, LLC.
26102  *
26103  * Originally Released Under LGPL - original licence link has changed is not relivant.
26104  *
26105  * Fork - LGPL
26106  * <script type="text/javascript">
26107  */
26108
26109 /**
26110  * @class Roo.data.ScriptTagProxy
26111  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26112  * other than the originating domain of the running page.<br><br>
26113  * <p>
26114  * <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
26115  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26116  * <p>
26117  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26118  * source code that is used as the source inside a &lt;script> tag.<br><br>
26119  * <p>
26120  * In order for the browser to process the returned data, the server must wrap the data object
26121  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26122  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26123  * depending on whether the callback name was passed:
26124  * <p>
26125  * <pre><code>
26126 boolean scriptTag = false;
26127 String cb = request.getParameter("callback");
26128 if (cb != null) {
26129     scriptTag = true;
26130     response.setContentType("text/javascript");
26131 } else {
26132     response.setContentType("application/x-json");
26133 }
26134 Writer out = response.getWriter();
26135 if (scriptTag) {
26136     out.write(cb + "(");
26137 }
26138 out.print(dataBlock.toJsonString());
26139 if (scriptTag) {
26140     out.write(");");
26141 }
26142 </pre></code>
26143  *
26144  * @constructor
26145  * @param {Object} config A configuration object.
26146  */
26147 Roo.data.ScriptTagProxy = function(config){
26148     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26149     Roo.apply(this, config);
26150     this.head = document.getElementsByTagName("head")[0];
26151 };
26152
26153 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26154
26155 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26156     /**
26157      * @cfg {String} url The URL from which to request the data object.
26158      */
26159     /**
26160      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26161      */
26162     timeout : 30000,
26163     /**
26164      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26165      * the server the name of the callback function set up by the load call to process the returned data object.
26166      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26167      * javascript output which calls this named function passing the data object as its only parameter.
26168      */
26169     callbackParam : "callback",
26170     /**
26171      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26172      * name to the request.
26173      */
26174     nocache : true,
26175
26176     /**
26177      * Load data from the configured URL, read the data object into
26178      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26179      * process that block using the passed callback.
26180      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26181      * for the request to the remote server.
26182      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26183      * object into a block of Roo.data.Records.
26184      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26185      * The function must be passed <ul>
26186      * <li>The Record block object</li>
26187      * <li>The "arg" argument from the load function</li>
26188      * <li>A boolean success indicator</li>
26189      * </ul>
26190      * @param {Object} scope The scope in which to call the callback
26191      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26192      */
26193     load : function(params, reader, callback, scope, arg){
26194         if(this.fireEvent("beforeload", this, params) !== false){
26195
26196             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26197
26198             var url = this.url;
26199             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26200             if(this.nocache){
26201                 url += "&_dc=" + (new Date().getTime());
26202             }
26203             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26204             var trans = {
26205                 id : transId,
26206                 cb : "stcCallback"+transId,
26207                 scriptId : "stcScript"+transId,
26208                 params : params,
26209                 arg : arg,
26210                 url : url,
26211                 callback : callback,
26212                 scope : scope,
26213                 reader : reader
26214             };
26215             var conn = this;
26216
26217             window[trans.cb] = function(o){
26218                 conn.handleResponse(o, trans);
26219             };
26220
26221             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26222
26223             if(this.autoAbort !== false){
26224                 this.abort();
26225             }
26226
26227             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26228
26229             var script = document.createElement("script");
26230             script.setAttribute("src", url);
26231             script.setAttribute("type", "text/javascript");
26232             script.setAttribute("id", trans.scriptId);
26233             this.head.appendChild(script);
26234
26235             this.trans = trans;
26236         }else{
26237             callback.call(scope||this, null, arg, false);
26238         }
26239     },
26240
26241     // private
26242     isLoading : function(){
26243         return this.trans ? true : false;
26244     },
26245
26246     /**
26247      * Abort the current server request.
26248      */
26249     abort : function(){
26250         if(this.isLoading()){
26251             this.destroyTrans(this.trans);
26252         }
26253     },
26254
26255     // private
26256     destroyTrans : function(trans, isLoaded){
26257         this.head.removeChild(document.getElementById(trans.scriptId));
26258         clearTimeout(trans.timeoutId);
26259         if(isLoaded){
26260             window[trans.cb] = undefined;
26261             try{
26262                 delete window[trans.cb];
26263             }catch(e){}
26264         }else{
26265             // if hasn't been loaded, wait for load to remove it to prevent script error
26266             window[trans.cb] = function(){
26267                 window[trans.cb] = undefined;
26268                 try{
26269                     delete window[trans.cb];
26270                 }catch(e){}
26271             };
26272         }
26273     },
26274
26275     // private
26276     handleResponse : function(o, trans){
26277         this.trans = false;
26278         this.destroyTrans(trans, true);
26279         var result;
26280         try {
26281             result = trans.reader.readRecords(o);
26282         }catch(e){
26283             this.fireEvent("loadexception", this, o, trans.arg, e);
26284             trans.callback.call(trans.scope||window, null, trans.arg, false);
26285             return;
26286         }
26287         this.fireEvent("load", this, o, trans.arg);
26288         trans.callback.call(trans.scope||window, result, trans.arg, true);
26289     },
26290
26291     // private
26292     handleFailure : function(trans){
26293         this.trans = false;
26294         this.destroyTrans(trans, false);
26295         this.fireEvent("loadexception", this, null, trans.arg);
26296         trans.callback.call(trans.scope||window, null, trans.arg, false);
26297     }
26298 });/*
26299  * Based on:
26300  * Ext JS Library 1.1.1
26301  * Copyright(c) 2006-2007, Ext JS, LLC.
26302  *
26303  * Originally Released Under LGPL - original licence link has changed is not relivant.
26304  *
26305  * Fork - LGPL
26306  * <script type="text/javascript">
26307  */
26308
26309 /**
26310  * @class Roo.data.JsonReader
26311  * @extends Roo.data.DataReader
26312  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26313  * based on mappings in a provided Roo.data.Record constructor.
26314  * 
26315  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26316  * in the reply previously. 
26317  * 
26318  * <p>
26319  * Example code:
26320  * <pre><code>
26321 var RecordDef = Roo.data.Record.create([
26322     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26323     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26324 ]);
26325 var myReader = new Roo.data.JsonReader({
26326     totalProperty: "results",    // The property which contains the total dataset size (optional)
26327     root: "rows",                // The property which contains an Array of row objects
26328     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26329 }, RecordDef);
26330 </code></pre>
26331  * <p>
26332  * This would consume a JSON file like this:
26333  * <pre><code>
26334 { 'results': 2, 'rows': [
26335     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26336     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26337 }
26338 </code></pre>
26339  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26340  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26341  * paged from the remote server.
26342  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26343  * @cfg {String} root name of the property which contains the Array of row objects.
26344  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26345  * @cfg {Array} fields Array of field definition objects
26346  * @constructor
26347  * Create a new JsonReader
26348  * @param {Object} meta Metadata configuration options
26349  * @param {Object} recordType Either an Array of field definition objects,
26350  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26351  */
26352 Roo.data.JsonReader = function(meta, recordType){
26353     
26354     meta = meta || {};
26355     // set some defaults:
26356     Roo.applyIf(meta, {
26357         totalProperty: 'total',
26358         successProperty : 'success',
26359         root : 'data',
26360         id : 'id'
26361     });
26362     
26363     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26364 };
26365 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26366     
26367     readerType : 'Json',
26368     
26369     /**
26370      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26371      * Used by Store query builder to append _requestMeta to params.
26372      * 
26373      */
26374     metaFromRemote : false,
26375     /**
26376      * This method is only used by a DataProxy which has retrieved data from a remote server.
26377      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26378      * @return {Object} data A data block which is used by an Roo.data.Store object as
26379      * a cache of Roo.data.Records.
26380      */
26381     read : function(response){
26382         var json = response.responseText;
26383        
26384         var o = /* eval:var:o */ eval("("+json+")");
26385         if(!o) {
26386             throw {message: "JsonReader.read: Json object not found"};
26387         }
26388         
26389         if(o.metaData){
26390             
26391             delete this.ef;
26392             this.metaFromRemote = true;
26393             this.meta = o.metaData;
26394             this.recordType = Roo.data.Record.create(o.metaData.fields);
26395             this.onMetaChange(this.meta, this.recordType, o);
26396         }
26397         return this.readRecords(o);
26398     },
26399
26400     // private function a store will implement
26401     onMetaChange : function(meta, recordType, o){
26402
26403     },
26404
26405     /**
26406          * @ignore
26407          */
26408     simpleAccess: function(obj, subsc) {
26409         return obj[subsc];
26410     },
26411
26412         /**
26413          * @ignore
26414          */
26415     getJsonAccessor: function(){
26416         var re = /[\[\.]/;
26417         return function(expr) {
26418             try {
26419                 return(re.test(expr))
26420                     ? new Function("obj", "return obj." + expr)
26421                     : function(obj){
26422                         return obj[expr];
26423                     };
26424             } catch(e){}
26425             return Roo.emptyFn;
26426         };
26427     }(),
26428
26429     /**
26430      * Create a data block containing Roo.data.Records from an XML document.
26431      * @param {Object} o An object which contains an Array of row objects in the property specified
26432      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26433      * which contains the total size of the dataset.
26434      * @return {Object} data A data block which is used by an Roo.data.Store object as
26435      * a cache of Roo.data.Records.
26436      */
26437     readRecords : function(o){
26438         /**
26439          * After any data loads, the raw JSON data is available for further custom processing.
26440          * @type Object
26441          */
26442         this.o = o;
26443         var s = this.meta, Record = this.recordType,
26444             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26445
26446 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26447         if (!this.ef) {
26448             if(s.totalProperty) {
26449                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26450                 }
26451                 if(s.successProperty) {
26452                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26453                 }
26454                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26455                 if (s.id) {
26456                         var g = this.getJsonAccessor(s.id);
26457                         this.getId = function(rec) {
26458                                 var r = g(rec);  
26459                                 return (r === undefined || r === "") ? null : r;
26460                         };
26461                 } else {
26462                         this.getId = function(){return null;};
26463                 }
26464             this.ef = [];
26465             for(var jj = 0; jj < fl; jj++){
26466                 f = fi[jj];
26467                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26468                 this.ef[jj] = this.getJsonAccessor(map);
26469             }
26470         }
26471
26472         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26473         if(s.totalProperty){
26474             var vt = parseInt(this.getTotal(o), 10);
26475             if(!isNaN(vt)){
26476                 totalRecords = vt;
26477             }
26478         }
26479         if(s.successProperty){
26480             var vs = this.getSuccess(o);
26481             if(vs === false || vs === 'false'){
26482                 success = false;
26483             }
26484         }
26485         var records = [];
26486         for(var i = 0; i < c; i++){
26487             var n = root[i];
26488             var values = {};
26489             var id = this.getId(n);
26490             for(var j = 0; j < fl; j++){
26491                 f = fi[j];
26492                                 var v = this.ef[j](n);
26493                                 if (!f.convert) {
26494                                         Roo.log('missing convert for ' + f.name);
26495                                         Roo.log(f);
26496                                         continue;
26497                                 }
26498                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26499             }
26500                         if (!Record) {
26501                                 return {
26502                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26503                                         success : false,
26504                                         records : [],
26505                                         totalRecords : 0
26506                                 };
26507                         }
26508             var record = new Record(values, id);
26509             record.json = n;
26510             records[i] = record;
26511         }
26512         return {
26513             raw : o,
26514             success : success,
26515             records : records,
26516             totalRecords : totalRecords
26517         };
26518     },
26519     // used when loading children.. @see loadDataFromChildren
26520     toLoadData: function(rec)
26521     {
26522         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26523         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26524         return { data : data, total : data.length };
26525         
26526     }
26527 });/*
26528  * Based on:
26529  * Ext JS Library 1.1.1
26530  * Copyright(c) 2006-2007, Ext JS, LLC.
26531  *
26532  * Originally Released Under LGPL - original licence link has changed is not relivant.
26533  *
26534  * Fork - LGPL
26535  * <script type="text/javascript">
26536  */
26537
26538 /**
26539  * @class Roo.data.XmlReader
26540  * @extends Roo.data.DataReader
26541  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26542  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26543  * <p>
26544  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26545  * header in the HTTP response must be set to "text/xml".</em>
26546  * <p>
26547  * Example code:
26548  * <pre><code>
26549 var RecordDef = Roo.data.Record.create([
26550    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26551    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26552 ]);
26553 var myReader = new Roo.data.XmlReader({
26554    totalRecords: "results", // The element which contains the total dataset size (optional)
26555    record: "row",           // The repeated element which contains row information
26556    id: "id"                 // The element within the row that provides an ID for the record (optional)
26557 }, RecordDef);
26558 </code></pre>
26559  * <p>
26560  * This would consume an XML file like this:
26561  * <pre><code>
26562 &lt;?xml?>
26563 &lt;dataset>
26564  &lt;results>2&lt;/results>
26565  &lt;row>
26566    &lt;id>1&lt;/id>
26567    &lt;name>Bill&lt;/name>
26568    &lt;occupation>Gardener&lt;/occupation>
26569  &lt;/row>
26570  &lt;row>
26571    &lt;id>2&lt;/id>
26572    &lt;name>Ben&lt;/name>
26573    &lt;occupation>Horticulturalist&lt;/occupation>
26574  &lt;/row>
26575 &lt;/dataset>
26576 </code></pre>
26577  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26578  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26579  * paged from the remote server.
26580  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26581  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26582  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26583  * a record identifier value.
26584  * @constructor
26585  * Create a new XmlReader
26586  * @param {Object} meta Metadata configuration options
26587  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26588  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26589  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26590  */
26591 Roo.data.XmlReader = function(meta, recordType){
26592     meta = meta || {};
26593     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26594 };
26595 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26596     
26597     readerType : 'Xml',
26598     
26599     /**
26600      * This method is only used by a DataProxy which has retrieved data from a remote server.
26601          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26602          * to contain a method called 'responseXML' that returns an XML document object.
26603      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26604      * a cache of Roo.data.Records.
26605      */
26606     read : function(response){
26607         var doc = response.responseXML;
26608         if(!doc) {
26609             throw {message: "XmlReader.read: XML Document not available"};
26610         }
26611         return this.readRecords(doc);
26612     },
26613
26614     /**
26615      * Create a data block containing Roo.data.Records from an XML document.
26616          * @param {Object} doc A parsed XML document.
26617      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26618      * a cache of Roo.data.Records.
26619      */
26620     readRecords : function(doc){
26621         /**
26622          * After any data loads/reads, the raw XML Document is available for further custom processing.
26623          * @type XMLDocument
26624          */
26625         this.xmlData = doc;
26626         var root = doc.documentElement || doc;
26627         var q = Roo.DomQuery;
26628         var recordType = this.recordType, fields = recordType.prototype.fields;
26629         var sid = this.meta.id;
26630         var totalRecords = 0, success = true;
26631         if(this.meta.totalRecords){
26632             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26633         }
26634         
26635         if(this.meta.success){
26636             var sv = q.selectValue(this.meta.success, root, true);
26637             success = sv !== false && sv !== 'false';
26638         }
26639         var records = [];
26640         var ns = q.select(this.meta.record, root);
26641         for(var i = 0, len = ns.length; i < len; i++) {
26642                 var n = ns[i];
26643                 var values = {};
26644                 var id = sid ? q.selectValue(sid, n) : undefined;
26645                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26646                     var f = fields.items[j];
26647                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26648                     v = f.convert(v);
26649                     values[f.name] = v;
26650                 }
26651                 var record = new recordType(values, id);
26652                 record.node = n;
26653                 records[records.length] = record;
26654             }
26655
26656             return {
26657                 success : success,
26658                 records : records,
26659                 totalRecords : totalRecords || records.length
26660             };
26661     }
26662 });/*
26663  * Based on:
26664  * Ext JS Library 1.1.1
26665  * Copyright(c) 2006-2007, Ext JS, LLC.
26666  *
26667  * Originally Released Under LGPL - original licence link has changed is not relivant.
26668  *
26669  * Fork - LGPL
26670  * <script type="text/javascript">
26671  */
26672
26673 /**
26674  * @class Roo.data.ArrayReader
26675  * @extends Roo.data.DataReader
26676  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26677  * Each element of that Array represents a row of data fields. The
26678  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26679  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26680  * <p>
26681  * Example code:.
26682  * <pre><code>
26683 var RecordDef = Roo.data.Record.create([
26684     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26685     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26686 ]);
26687 var myReader = new Roo.data.ArrayReader({
26688     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26689 }, RecordDef);
26690 </code></pre>
26691  * <p>
26692  * This would consume an Array like this:
26693  * <pre><code>
26694 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26695   </code></pre>
26696  
26697  * @constructor
26698  * Create a new JsonReader
26699  * @param {Object} meta Metadata configuration options.
26700  * @param {Object|Array} recordType Either an Array of field definition objects
26701  * 
26702  * @cfg {Array} fields Array of field definition objects
26703  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26704  * as specified to {@link Roo.data.Record#create},
26705  * or an {@link Roo.data.Record} object
26706  *
26707  * 
26708  * created using {@link Roo.data.Record#create}.
26709  */
26710 Roo.data.ArrayReader = function(meta, recordType)
26711 {    
26712     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26713 };
26714
26715 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26716     
26717       /**
26718      * Create a data block containing Roo.data.Records from an XML document.
26719      * @param {Object} o An Array of row objects which represents the dataset.
26720      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26721      * a cache of Roo.data.Records.
26722      */
26723     readRecords : function(o)
26724     {
26725         var sid = this.meta ? this.meta.id : null;
26726         var recordType = this.recordType, fields = recordType.prototype.fields;
26727         var records = [];
26728         var root = o;
26729         for(var i = 0; i < root.length; i++){
26730             var n = root[i];
26731             var values = {};
26732             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26733             for(var j = 0, jlen = fields.length; j < jlen; j++){
26734                 var f = fields.items[j];
26735                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26736                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26737                 v = f.convert(v);
26738                 values[f.name] = v;
26739             }
26740             var record = new recordType(values, id);
26741             record.json = n;
26742             records[records.length] = record;
26743         }
26744         return {
26745             records : records,
26746             totalRecords : records.length
26747         };
26748     },
26749     // used when loading children.. @see loadDataFromChildren
26750     toLoadData: function(rec)
26751     {
26752         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26753         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26754         
26755     }
26756     
26757     
26758 });/*
26759  * Based on:
26760  * Ext JS Library 1.1.1
26761  * Copyright(c) 2006-2007, Ext JS, LLC.
26762  *
26763  * Originally Released Under LGPL - original licence link has changed is not relivant.
26764  *
26765  * Fork - LGPL
26766  * <script type="text/javascript">
26767  */
26768
26769
26770 /**
26771  * @class Roo.data.Tree
26772  * @extends Roo.util.Observable
26773  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26774  * in the tree have most standard DOM functionality.
26775  * @constructor
26776  * @param {Node} root (optional) The root node
26777  */
26778 Roo.data.Tree = function(root){
26779    this.nodeHash = {};
26780    /**
26781     * The root node for this tree
26782     * @type Node
26783     */
26784    this.root = null;
26785    if(root){
26786        this.setRootNode(root);
26787    }
26788    this.addEvents({
26789        /**
26790         * @event append
26791         * Fires when a new child node is appended to a node in this tree.
26792         * @param {Tree} tree The owner tree
26793         * @param {Node} parent The parent node
26794         * @param {Node} node The newly appended node
26795         * @param {Number} index The index of the newly appended node
26796         */
26797        "append" : true,
26798        /**
26799         * @event remove
26800         * Fires when a child node is removed from a node in this tree.
26801         * @param {Tree} tree The owner tree
26802         * @param {Node} parent The parent node
26803         * @param {Node} node The child node removed
26804         */
26805        "remove" : true,
26806        /**
26807         * @event move
26808         * Fires when a node is moved to a new location in the tree
26809         * @param {Tree} tree The owner tree
26810         * @param {Node} node The node moved
26811         * @param {Node} oldParent The old parent of this node
26812         * @param {Node} newParent The new parent of this node
26813         * @param {Number} index The index it was moved to
26814         */
26815        "move" : true,
26816        /**
26817         * @event insert
26818         * Fires when a new child node is inserted in a node in this tree.
26819         * @param {Tree} tree The owner tree
26820         * @param {Node} parent The parent node
26821         * @param {Node} node The child node inserted
26822         * @param {Node} refNode The child node the node was inserted before
26823         */
26824        "insert" : true,
26825        /**
26826         * @event beforeappend
26827         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26828         * @param {Tree} tree The owner tree
26829         * @param {Node} parent The parent node
26830         * @param {Node} node The child node to be appended
26831         */
26832        "beforeappend" : true,
26833        /**
26834         * @event beforeremove
26835         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26836         * @param {Tree} tree The owner tree
26837         * @param {Node} parent The parent node
26838         * @param {Node} node The child node to be removed
26839         */
26840        "beforeremove" : true,
26841        /**
26842         * @event beforemove
26843         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26844         * @param {Tree} tree The owner tree
26845         * @param {Node} node The node being moved
26846         * @param {Node} oldParent The parent of the node
26847         * @param {Node} newParent The new parent the node is moving to
26848         * @param {Number} index The index it is being moved to
26849         */
26850        "beforemove" : true,
26851        /**
26852         * @event beforeinsert
26853         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26854         * @param {Tree} tree The owner tree
26855         * @param {Node} parent The parent node
26856         * @param {Node} node The child node to be inserted
26857         * @param {Node} refNode The child node the node is being inserted before
26858         */
26859        "beforeinsert" : true
26860    });
26861
26862     Roo.data.Tree.superclass.constructor.call(this);
26863 };
26864
26865 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26866     pathSeparator: "/",
26867
26868     proxyNodeEvent : function(){
26869         return this.fireEvent.apply(this, arguments);
26870     },
26871
26872     /**
26873      * Returns the root node for this tree.
26874      * @return {Node}
26875      */
26876     getRootNode : function(){
26877         return this.root;
26878     },
26879
26880     /**
26881      * Sets the root node for this tree.
26882      * @param {Node} node
26883      * @return {Node}
26884      */
26885     setRootNode : function(node){
26886         this.root = node;
26887         node.ownerTree = this;
26888         node.isRoot = true;
26889         this.registerNode(node);
26890         return node;
26891     },
26892
26893     /**
26894      * Gets a node in this tree by its id.
26895      * @param {String} id
26896      * @return {Node}
26897      */
26898     getNodeById : function(id){
26899         return this.nodeHash[id];
26900     },
26901
26902     registerNode : function(node){
26903         this.nodeHash[node.id] = node;
26904     },
26905
26906     unregisterNode : function(node){
26907         delete this.nodeHash[node.id];
26908     },
26909
26910     toString : function(){
26911         return "[Tree"+(this.id?" "+this.id:"")+"]";
26912     }
26913 });
26914
26915 /**
26916  * @class Roo.data.Node
26917  * @extends Roo.util.Observable
26918  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26919  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26920  * @constructor
26921  * @param {Object} attributes The attributes/config for the node
26922  */
26923 Roo.data.Node = function(attributes){
26924     /**
26925      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26926      * @type {Object}
26927      */
26928     this.attributes = attributes || {};
26929     this.leaf = this.attributes.leaf;
26930     /**
26931      * The node id. @type String
26932      */
26933     this.id = this.attributes.id;
26934     if(!this.id){
26935         this.id = Roo.id(null, "ynode-");
26936         this.attributes.id = this.id;
26937     }
26938      
26939     
26940     /**
26941      * All child nodes of this node. @type Array
26942      */
26943     this.childNodes = [];
26944     if(!this.childNodes.indexOf){ // indexOf is a must
26945         this.childNodes.indexOf = function(o){
26946             for(var i = 0, len = this.length; i < len; i++){
26947                 if(this[i] == o) {
26948                     return i;
26949                 }
26950             }
26951             return -1;
26952         };
26953     }
26954     /**
26955      * The parent node for this node. @type Node
26956      */
26957     this.parentNode = null;
26958     /**
26959      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26960      */
26961     this.firstChild = null;
26962     /**
26963      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26964      */
26965     this.lastChild = null;
26966     /**
26967      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26968      */
26969     this.previousSibling = null;
26970     /**
26971      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26972      */
26973     this.nextSibling = null;
26974
26975     this.addEvents({
26976        /**
26977         * @event append
26978         * Fires when a new child node is appended
26979         * @param {Tree} tree The owner tree
26980         * @param {Node} this This node
26981         * @param {Node} node The newly appended node
26982         * @param {Number} index The index of the newly appended node
26983         */
26984        "append" : true,
26985        /**
26986         * @event remove
26987         * Fires when a child node is removed
26988         * @param {Tree} tree The owner tree
26989         * @param {Node} this This node
26990         * @param {Node} node The removed node
26991         */
26992        "remove" : true,
26993        /**
26994         * @event move
26995         * Fires when this node is moved to a new location in the tree
26996         * @param {Tree} tree The owner tree
26997         * @param {Node} this This node
26998         * @param {Node} oldParent The old parent of this node
26999         * @param {Node} newParent The new parent of this node
27000         * @param {Number} index The index it was moved to
27001         */
27002        "move" : true,
27003        /**
27004         * @event insert
27005         * Fires when a new child node is inserted.
27006         * @param {Tree} tree The owner tree
27007         * @param {Node} this This node
27008         * @param {Node} node The child node inserted
27009         * @param {Node} refNode The child node the node was inserted before
27010         */
27011        "insert" : true,
27012        /**
27013         * @event beforeappend
27014         * Fires before a new child is appended, return false to cancel the append.
27015         * @param {Tree} tree The owner tree
27016         * @param {Node} this This node
27017         * @param {Node} node The child node to be appended
27018         */
27019        "beforeappend" : true,
27020        /**
27021         * @event beforeremove
27022         * Fires before a child is removed, return false to cancel the remove.
27023         * @param {Tree} tree The owner tree
27024         * @param {Node} this This node
27025         * @param {Node} node The child node to be removed
27026         */
27027        "beforeremove" : true,
27028        /**
27029         * @event beforemove
27030         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27031         * @param {Tree} tree The owner tree
27032         * @param {Node} this This node
27033         * @param {Node} oldParent The parent of this node
27034         * @param {Node} newParent The new parent this node is moving to
27035         * @param {Number} index The index it is being moved to
27036         */
27037        "beforemove" : true,
27038        /**
27039         * @event beforeinsert
27040         * Fires before a new child is inserted, return false to cancel the insert.
27041         * @param {Tree} tree The owner tree
27042         * @param {Node} this This node
27043         * @param {Node} node The child node to be inserted
27044         * @param {Node} refNode The child node the node is being inserted before
27045         */
27046        "beforeinsert" : true
27047    });
27048     this.listeners = this.attributes.listeners;
27049     Roo.data.Node.superclass.constructor.call(this);
27050 };
27051
27052 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27053     fireEvent : function(evtName){
27054         // first do standard event for this node
27055         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27056             return false;
27057         }
27058         // then bubble it up to the tree if the event wasn't cancelled
27059         var ot = this.getOwnerTree();
27060         if(ot){
27061             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27062                 return false;
27063             }
27064         }
27065         return true;
27066     },
27067
27068     /**
27069      * Returns true if this node is a leaf
27070      * @return {Boolean}
27071      */
27072     isLeaf : function(){
27073         return this.leaf === true;
27074     },
27075
27076     // private
27077     setFirstChild : function(node){
27078         this.firstChild = node;
27079     },
27080
27081     //private
27082     setLastChild : function(node){
27083         this.lastChild = node;
27084     },
27085
27086
27087     /**
27088      * Returns true if this node is the last child of its parent
27089      * @return {Boolean}
27090      */
27091     isLast : function(){
27092        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27093     },
27094
27095     /**
27096      * Returns true if this node is the first child of its parent
27097      * @return {Boolean}
27098      */
27099     isFirst : function(){
27100        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27101     },
27102
27103     hasChildNodes : function(){
27104         return !this.isLeaf() && this.childNodes.length > 0;
27105     },
27106
27107     /**
27108      * Insert node(s) as the last child node of this node.
27109      * @param {Node/Array} node The node or Array of nodes to append
27110      * @return {Node} The appended node if single append, or null if an array was passed
27111      */
27112     appendChild : function(node){
27113         var multi = false;
27114         if(node instanceof Array){
27115             multi = node;
27116         }else if(arguments.length > 1){
27117             multi = arguments;
27118         }
27119         
27120         // if passed an array or multiple args do them one by one
27121         if(multi){
27122             for(var i = 0, len = multi.length; i < len; i++) {
27123                 this.appendChild(multi[i]);
27124             }
27125         }else{
27126             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27127                 return false;
27128             }
27129             var index = this.childNodes.length;
27130             var oldParent = node.parentNode;
27131             // it's a move, make sure we move it cleanly
27132             if(oldParent){
27133                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27134                     return false;
27135                 }
27136                 oldParent.removeChild(node);
27137             }
27138             
27139             index = this.childNodes.length;
27140             if(index == 0){
27141                 this.setFirstChild(node);
27142             }
27143             this.childNodes.push(node);
27144             node.parentNode = this;
27145             var ps = this.childNodes[index-1];
27146             if(ps){
27147                 node.previousSibling = ps;
27148                 ps.nextSibling = node;
27149             }else{
27150                 node.previousSibling = null;
27151             }
27152             node.nextSibling = null;
27153             this.setLastChild(node);
27154             node.setOwnerTree(this.getOwnerTree());
27155             this.fireEvent("append", this.ownerTree, this, node, index);
27156             if(this.ownerTree) {
27157                 this.ownerTree.fireEvent("appendnode", this, node, index);
27158             }
27159             if(oldParent){
27160                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27161             }
27162             return node;
27163         }
27164     },
27165
27166     /**
27167      * Removes a child node from this node.
27168      * @param {Node} node The node to remove
27169      * @return {Node} The removed node
27170      */
27171     removeChild : function(node){
27172         var index = this.childNodes.indexOf(node);
27173         if(index == -1){
27174             return false;
27175         }
27176         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27177             return false;
27178         }
27179
27180         // remove it from childNodes collection
27181         this.childNodes.splice(index, 1);
27182
27183         // update siblings
27184         if(node.previousSibling){
27185             node.previousSibling.nextSibling = node.nextSibling;
27186         }
27187         if(node.nextSibling){
27188             node.nextSibling.previousSibling = node.previousSibling;
27189         }
27190
27191         // update child refs
27192         if(this.firstChild == node){
27193             this.setFirstChild(node.nextSibling);
27194         }
27195         if(this.lastChild == node){
27196             this.setLastChild(node.previousSibling);
27197         }
27198
27199         node.setOwnerTree(null);
27200         // clear any references from the node
27201         node.parentNode = null;
27202         node.previousSibling = null;
27203         node.nextSibling = null;
27204         this.fireEvent("remove", this.ownerTree, this, node);
27205         return node;
27206     },
27207
27208     /**
27209      * Inserts the first node before the second node in this nodes childNodes collection.
27210      * @param {Node} node The node to insert
27211      * @param {Node} refNode The node to insert before (if null the node is appended)
27212      * @return {Node} The inserted node
27213      */
27214     insertBefore : function(node, refNode){
27215         if(!refNode){ // like standard Dom, refNode can be null for append
27216             return this.appendChild(node);
27217         }
27218         // nothing to do
27219         if(node == refNode){
27220             return false;
27221         }
27222
27223         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27224             return false;
27225         }
27226         var index = this.childNodes.indexOf(refNode);
27227         var oldParent = node.parentNode;
27228         var refIndex = index;
27229
27230         // when moving internally, indexes will change after remove
27231         if(oldParent == this && this.childNodes.indexOf(node) < index){
27232             refIndex--;
27233         }
27234
27235         // it's a move, make sure we move it cleanly
27236         if(oldParent){
27237             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27238                 return false;
27239             }
27240             oldParent.removeChild(node);
27241         }
27242         if(refIndex == 0){
27243             this.setFirstChild(node);
27244         }
27245         this.childNodes.splice(refIndex, 0, node);
27246         node.parentNode = this;
27247         var ps = this.childNodes[refIndex-1];
27248         if(ps){
27249             node.previousSibling = ps;
27250             ps.nextSibling = node;
27251         }else{
27252             node.previousSibling = null;
27253         }
27254         node.nextSibling = refNode;
27255         refNode.previousSibling = node;
27256         node.setOwnerTree(this.getOwnerTree());
27257         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27258         if(oldParent){
27259             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27260         }
27261         return node;
27262     },
27263
27264     /**
27265      * Returns the child node at the specified index.
27266      * @param {Number} index
27267      * @return {Node}
27268      */
27269     item : function(index){
27270         return this.childNodes[index];
27271     },
27272
27273     /**
27274      * Replaces one child node in this node with another.
27275      * @param {Node} newChild The replacement node
27276      * @param {Node} oldChild The node to replace
27277      * @return {Node} The replaced node
27278      */
27279     replaceChild : function(newChild, oldChild){
27280         this.insertBefore(newChild, oldChild);
27281         this.removeChild(oldChild);
27282         return oldChild;
27283     },
27284
27285     /**
27286      * Returns the index of a child node
27287      * @param {Node} node
27288      * @return {Number} The index of the node or -1 if it was not found
27289      */
27290     indexOf : function(child){
27291         return this.childNodes.indexOf(child);
27292     },
27293
27294     /**
27295      * Returns the tree this node is in.
27296      * @return {Tree}
27297      */
27298     getOwnerTree : function(){
27299         // if it doesn't have one, look for one
27300         if(!this.ownerTree){
27301             var p = this;
27302             while(p){
27303                 if(p.ownerTree){
27304                     this.ownerTree = p.ownerTree;
27305                     break;
27306                 }
27307                 p = p.parentNode;
27308             }
27309         }
27310         return this.ownerTree;
27311     },
27312
27313     /**
27314      * Returns depth of this node (the root node has a depth of 0)
27315      * @return {Number}
27316      */
27317     getDepth : function(){
27318         var depth = 0;
27319         var p = this;
27320         while(p.parentNode){
27321             ++depth;
27322             p = p.parentNode;
27323         }
27324         return depth;
27325     },
27326
27327     // private
27328     setOwnerTree : function(tree){
27329         // if it's move, we need to update everyone
27330         if(tree != this.ownerTree){
27331             if(this.ownerTree){
27332                 this.ownerTree.unregisterNode(this);
27333             }
27334             this.ownerTree = tree;
27335             var cs = this.childNodes;
27336             for(var i = 0, len = cs.length; i < len; i++) {
27337                 cs[i].setOwnerTree(tree);
27338             }
27339             if(tree){
27340                 tree.registerNode(this);
27341             }
27342         }
27343     },
27344
27345     /**
27346      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27347      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27348      * @return {String} The path
27349      */
27350     getPath : function(attr){
27351         attr = attr || "id";
27352         var p = this.parentNode;
27353         var b = [this.attributes[attr]];
27354         while(p){
27355             b.unshift(p.attributes[attr]);
27356             p = p.parentNode;
27357         }
27358         var sep = this.getOwnerTree().pathSeparator;
27359         return sep + b.join(sep);
27360     },
27361
27362     /**
27363      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27364      * function call will be the scope provided or the current node. The arguments to the function
27365      * will be the args provided or the current node. If the function returns false at any point,
27366      * the bubble is stopped.
27367      * @param {Function} fn The function to call
27368      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27369      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27370      */
27371     bubble : function(fn, scope, args){
27372         var p = this;
27373         while(p){
27374             if(fn.call(scope || p, args || p) === false){
27375                 break;
27376             }
27377             p = p.parentNode;
27378         }
27379     },
27380
27381     /**
27382      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27383      * function call will be the scope provided or the current node. The arguments to the function
27384      * will be the args provided or the current node. If the function returns false at any point,
27385      * the cascade is stopped on that branch.
27386      * @param {Function} fn The function to call
27387      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27388      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27389      */
27390     cascade : function(fn, scope, args){
27391         if(fn.call(scope || this, args || this) !== false){
27392             var cs = this.childNodes;
27393             for(var i = 0, len = cs.length; i < len; i++) {
27394                 cs[i].cascade(fn, scope, args);
27395             }
27396         }
27397     },
27398
27399     /**
27400      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27401      * function call will be the scope provided or the current node. The arguments to the function
27402      * will be the args provided or the current node. If the function returns false at any point,
27403      * the iteration stops.
27404      * @param {Function} fn The function to call
27405      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27406      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27407      */
27408     eachChild : function(fn, scope, args){
27409         var cs = this.childNodes;
27410         for(var i = 0, len = cs.length; i < len; i++) {
27411                 if(fn.call(scope || this, args || cs[i]) === false){
27412                     break;
27413                 }
27414         }
27415     },
27416
27417     /**
27418      * Finds the first child that has the attribute with the specified value.
27419      * @param {String} attribute The attribute name
27420      * @param {Mixed} value The value to search for
27421      * @return {Node} The found child or null if none was found
27422      */
27423     findChild : function(attribute, value){
27424         var cs = this.childNodes;
27425         for(var i = 0, len = cs.length; i < len; i++) {
27426                 if(cs[i].attributes[attribute] == value){
27427                     return cs[i];
27428                 }
27429         }
27430         return null;
27431     },
27432
27433     /**
27434      * Finds the first child by a custom function. The child matches if the function passed
27435      * returns true.
27436      * @param {Function} fn
27437      * @param {Object} scope (optional)
27438      * @return {Node} The found child or null if none was found
27439      */
27440     findChildBy : function(fn, scope){
27441         var cs = this.childNodes;
27442         for(var i = 0, len = cs.length; i < len; i++) {
27443                 if(fn.call(scope||cs[i], cs[i]) === true){
27444                     return cs[i];
27445                 }
27446         }
27447         return null;
27448     },
27449
27450     /**
27451      * Sorts this nodes children using the supplied sort function
27452      * @param {Function} fn
27453      * @param {Object} scope (optional)
27454      */
27455     sort : function(fn, scope){
27456         var cs = this.childNodes;
27457         var len = cs.length;
27458         if(len > 0){
27459             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27460             cs.sort(sortFn);
27461             for(var i = 0; i < len; i++){
27462                 var n = cs[i];
27463                 n.previousSibling = cs[i-1];
27464                 n.nextSibling = cs[i+1];
27465                 if(i == 0){
27466                     this.setFirstChild(n);
27467                 }
27468                 if(i == len-1){
27469                     this.setLastChild(n);
27470                 }
27471             }
27472         }
27473     },
27474
27475     /**
27476      * Returns true if this node is an ancestor (at any point) of the passed node.
27477      * @param {Node} node
27478      * @return {Boolean}
27479      */
27480     contains : function(node){
27481         return node.isAncestor(this);
27482     },
27483
27484     /**
27485      * Returns true if the passed node is an ancestor (at any point) of this node.
27486      * @param {Node} node
27487      * @return {Boolean}
27488      */
27489     isAncestor : function(node){
27490         var p = this.parentNode;
27491         while(p){
27492             if(p == node){
27493                 return true;
27494             }
27495             p = p.parentNode;
27496         }
27497         return false;
27498     },
27499
27500     toString : function(){
27501         return "[Node"+(this.id?" "+this.id:"")+"]";
27502     }
27503 });/*
27504  * Based on:
27505  * Ext JS Library 1.1.1
27506  * Copyright(c) 2006-2007, Ext JS, LLC.
27507  *
27508  * Originally Released Under LGPL - original licence link has changed is not relivant.
27509  *
27510  * Fork - LGPL
27511  * <script type="text/javascript">
27512  */
27513
27514
27515 /**
27516  * @class Roo.Shadow
27517  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27518  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27519  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27520  * @constructor
27521  * Create a new Shadow
27522  * @param {Object} config The config object
27523  */
27524 Roo.Shadow = function(config){
27525     Roo.apply(this, config);
27526     if(typeof this.mode != "string"){
27527         this.mode = this.defaultMode;
27528     }
27529     var o = this.offset, a = {h: 0};
27530     var rad = Math.floor(this.offset/2);
27531     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27532         case "drop":
27533             a.w = 0;
27534             a.l = a.t = o;
27535             a.t -= 1;
27536             if(Roo.isIE){
27537                 a.l -= this.offset + rad;
27538                 a.t -= this.offset + rad;
27539                 a.w -= rad;
27540                 a.h -= rad;
27541                 a.t += 1;
27542             }
27543         break;
27544         case "sides":
27545             a.w = (o*2);
27546             a.l = -o;
27547             a.t = o-1;
27548             if(Roo.isIE){
27549                 a.l -= (this.offset - rad);
27550                 a.t -= this.offset + rad;
27551                 a.l += 1;
27552                 a.w -= (this.offset - rad)*2;
27553                 a.w -= rad + 1;
27554                 a.h -= 1;
27555             }
27556         break;
27557         case "frame":
27558             a.w = a.h = (o*2);
27559             a.l = a.t = -o;
27560             a.t += 1;
27561             a.h -= 2;
27562             if(Roo.isIE){
27563                 a.l -= (this.offset - rad);
27564                 a.t -= (this.offset - rad);
27565                 a.l += 1;
27566                 a.w -= (this.offset + rad + 1);
27567                 a.h -= (this.offset + rad);
27568                 a.h += 1;
27569             }
27570         break;
27571     };
27572
27573     this.adjusts = a;
27574 };
27575
27576 Roo.Shadow.prototype = {
27577     /**
27578      * @cfg {String} mode
27579      * The shadow display mode.  Supports the following options:<br />
27580      * sides: Shadow displays on both sides and bottom only<br />
27581      * frame: Shadow displays equally on all four sides<br />
27582      * drop: Traditional bottom-right drop shadow (default)
27583      */
27584     mode: false,
27585     /**
27586      * @cfg {String} offset
27587      * The number of pixels to offset the shadow from the element (defaults to 4)
27588      */
27589     offset: 4,
27590
27591     // private
27592     defaultMode: "drop",
27593
27594     /**
27595      * Displays the shadow under the target element
27596      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27597      */
27598     show : function(target){
27599         target = Roo.get(target);
27600         if(!this.el){
27601             this.el = Roo.Shadow.Pool.pull();
27602             if(this.el.dom.nextSibling != target.dom){
27603                 this.el.insertBefore(target);
27604             }
27605         }
27606         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27607         if(Roo.isIE){
27608             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27609         }
27610         this.realign(
27611             target.getLeft(true),
27612             target.getTop(true),
27613             target.getWidth(),
27614             target.getHeight()
27615         );
27616         this.el.dom.style.display = "block";
27617     },
27618
27619     /**
27620      * Returns true if the shadow is visible, else false
27621      */
27622     isVisible : function(){
27623         return this.el ? true : false;  
27624     },
27625
27626     /**
27627      * Direct alignment when values are already available. Show must be called at least once before
27628      * calling this method to ensure it is initialized.
27629      * @param {Number} left The target element left position
27630      * @param {Number} top The target element top position
27631      * @param {Number} width The target element width
27632      * @param {Number} height The target element height
27633      */
27634     realign : function(l, t, w, h){
27635         if(!this.el){
27636             return;
27637         }
27638         var a = this.adjusts, d = this.el.dom, s = d.style;
27639         var iea = 0;
27640         s.left = (l+a.l)+"px";
27641         s.top = (t+a.t)+"px";
27642         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27643  
27644         if(s.width != sws || s.height != shs){
27645             s.width = sws;
27646             s.height = shs;
27647             if(!Roo.isIE){
27648                 var cn = d.childNodes;
27649                 var sww = Math.max(0, (sw-12))+"px";
27650                 cn[0].childNodes[1].style.width = sww;
27651                 cn[1].childNodes[1].style.width = sww;
27652                 cn[2].childNodes[1].style.width = sww;
27653                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27654             }
27655         }
27656     },
27657
27658     /**
27659      * Hides this shadow
27660      */
27661     hide : function(){
27662         if(this.el){
27663             this.el.dom.style.display = "none";
27664             Roo.Shadow.Pool.push(this.el);
27665             delete this.el;
27666         }
27667     },
27668
27669     /**
27670      * Adjust the z-index of this shadow
27671      * @param {Number} zindex The new z-index
27672      */
27673     setZIndex : function(z){
27674         this.zIndex = z;
27675         if(this.el){
27676             this.el.setStyle("z-index", z);
27677         }
27678     }
27679 };
27680
27681 // Private utility class that manages the internal Shadow cache
27682 Roo.Shadow.Pool = function(){
27683     var p = [];
27684     var markup = Roo.isIE ?
27685                  '<div class="x-ie-shadow"></div>' :
27686                  '<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>';
27687     return {
27688         pull : function(){
27689             var sh = p.shift();
27690             if(!sh){
27691                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27692                 sh.autoBoxAdjust = false;
27693             }
27694             return sh;
27695         },
27696
27697         push : function(sh){
27698             p.push(sh);
27699         }
27700     };
27701 }();/*
27702  * Based on:
27703  * Ext JS Library 1.1.1
27704  * Copyright(c) 2006-2007, Ext JS, LLC.
27705  *
27706  * Originally Released Under LGPL - original licence link has changed is not relivant.
27707  *
27708  * Fork - LGPL
27709  * <script type="text/javascript">
27710  */
27711
27712
27713 /**
27714  * @class Roo.SplitBar
27715  * @extends Roo.util.Observable
27716  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27717  * <br><br>
27718  * Usage:
27719  * <pre><code>
27720 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27721                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27722 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27723 split.minSize = 100;
27724 split.maxSize = 600;
27725 split.animate = true;
27726 split.on('moved', splitterMoved);
27727 </code></pre>
27728  * @constructor
27729  * Create a new SplitBar
27730  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27731  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27732  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27733  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27734                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27735                         position of the SplitBar).
27736  */
27737 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27738     
27739     /** @private */
27740     this.el = Roo.get(dragElement, true);
27741     this.el.dom.unselectable = "on";
27742     /** @private */
27743     this.resizingEl = Roo.get(resizingElement, true);
27744
27745     /**
27746      * @private
27747      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27748      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27749      * @type Number
27750      */
27751     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27752     
27753     /**
27754      * The minimum size of the resizing element. (Defaults to 0)
27755      * @type Number
27756      */
27757     this.minSize = 0;
27758     
27759     /**
27760      * The maximum size of the resizing element. (Defaults to 2000)
27761      * @type Number
27762      */
27763     this.maxSize = 2000;
27764     
27765     /**
27766      * Whether to animate the transition to the new size
27767      * @type Boolean
27768      */
27769     this.animate = false;
27770     
27771     /**
27772      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27773      * @type Boolean
27774      */
27775     this.useShim = false;
27776     
27777     /** @private */
27778     this.shim = null;
27779     
27780     if(!existingProxy){
27781         /** @private */
27782         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27783     }else{
27784         this.proxy = Roo.get(existingProxy).dom;
27785     }
27786     /** @private */
27787     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27788     
27789     /** @private */
27790     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27791     
27792     /** @private */
27793     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27794     
27795     /** @private */
27796     this.dragSpecs = {};
27797     
27798     /**
27799      * @private The adapter to use to positon and resize elements
27800      */
27801     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27802     this.adapter.init(this);
27803     
27804     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27805         /** @private */
27806         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27807         this.el.addClass("x-splitbar-h");
27808     }else{
27809         /** @private */
27810         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27811         this.el.addClass("x-splitbar-v");
27812     }
27813     
27814     this.addEvents({
27815         /**
27816          * @event resize
27817          * Fires when the splitter is moved (alias for {@link #event-moved})
27818          * @param {Roo.SplitBar} this
27819          * @param {Number} newSize the new width or height
27820          */
27821         "resize" : true,
27822         /**
27823          * @event moved
27824          * Fires when the splitter is moved
27825          * @param {Roo.SplitBar} this
27826          * @param {Number} newSize the new width or height
27827          */
27828         "moved" : true,
27829         /**
27830          * @event beforeresize
27831          * Fires before the splitter is dragged
27832          * @param {Roo.SplitBar} this
27833          */
27834         "beforeresize" : true,
27835
27836         "beforeapply" : true
27837     });
27838
27839     Roo.util.Observable.call(this);
27840 };
27841
27842 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27843     onStartProxyDrag : function(x, y){
27844         this.fireEvent("beforeresize", this);
27845         if(!this.overlay){
27846             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27847             o.unselectable();
27848             o.enableDisplayMode("block");
27849             // all splitbars share the same overlay
27850             Roo.SplitBar.prototype.overlay = o;
27851         }
27852         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27853         this.overlay.show();
27854         Roo.get(this.proxy).setDisplayed("block");
27855         var size = this.adapter.getElementSize(this);
27856         this.activeMinSize = this.getMinimumSize();;
27857         this.activeMaxSize = this.getMaximumSize();;
27858         var c1 = size - this.activeMinSize;
27859         var c2 = Math.max(this.activeMaxSize - size, 0);
27860         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27861             this.dd.resetConstraints();
27862             this.dd.setXConstraint(
27863                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27864                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27865             );
27866             this.dd.setYConstraint(0, 0);
27867         }else{
27868             this.dd.resetConstraints();
27869             this.dd.setXConstraint(0, 0);
27870             this.dd.setYConstraint(
27871                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27872                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27873             );
27874          }
27875         this.dragSpecs.startSize = size;
27876         this.dragSpecs.startPoint = [x, y];
27877         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27878     },
27879     
27880     /** 
27881      * @private Called after the drag operation by the DDProxy
27882      */
27883     onEndProxyDrag : function(e){
27884         Roo.get(this.proxy).setDisplayed(false);
27885         var endPoint = Roo.lib.Event.getXY(e);
27886         if(this.overlay){
27887             this.overlay.hide();
27888         }
27889         var newSize;
27890         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27891             newSize = this.dragSpecs.startSize + 
27892                 (this.placement == Roo.SplitBar.LEFT ?
27893                     endPoint[0] - this.dragSpecs.startPoint[0] :
27894                     this.dragSpecs.startPoint[0] - endPoint[0]
27895                 );
27896         }else{
27897             newSize = this.dragSpecs.startSize + 
27898                 (this.placement == Roo.SplitBar.TOP ?
27899                     endPoint[1] - this.dragSpecs.startPoint[1] :
27900                     this.dragSpecs.startPoint[1] - endPoint[1]
27901                 );
27902         }
27903         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27904         if(newSize != this.dragSpecs.startSize){
27905             if(this.fireEvent('beforeapply', this, newSize) !== false){
27906                 this.adapter.setElementSize(this, newSize);
27907                 this.fireEvent("moved", this, newSize);
27908                 this.fireEvent("resize", this, newSize);
27909             }
27910         }
27911     },
27912     
27913     /**
27914      * Get the adapter this SplitBar uses
27915      * @return The adapter object
27916      */
27917     getAdapter : function(){
27918         return this.adapter;
27919     },
27920     
27921     /**
27922      * Set the adapter this SplitBar uses
27923      * @param {Object} adapter A SplitBar adapter object
27924      */
27925     setAdapter : function(adapter){
27926         this.adapter = adapter;
27927         this.adapter.init(this);
27928     },
27929     
27930     /**
27931      * Gets the minimum size for the resizing element
27932      * @return {Number} The minimum size
27933      */
27934     getMinimumSize : function(){
27935         return this.minSize;
27936     },
27937     
27938     /**
27939      * Sets the minimum size for the resizing element
27940      * @param {Number} minSize The minimum size
27941      */
27942     setMinimumSize : function(minSize){
27943         this.minSize = minSize;
27944     },
27945     
27946     /**
27947      * Gets the maximum size for the resizing element
27948      * @return {Number} The maximum size
27949      */
27950     getMaximumSize : function(){
27951         return this.maxSize;
27952     },
27953     
27954     /**
27955      * Sets the maximum size for the resizing element
27956      * @param {Number} maxSize The maximum size
27957      */
27958     setMaximumSize : function(maxSize){
27959         this.maxSize = maxSize;
27960     },
27961     
27962     /**
27963      * Sets the initialize size for the resizing element
27964      * @param {Number} size The initial size
27965      */
27966     setCurrentSize : function(size){
27967         var oldAnimate = this.animate;
27968         this.animate = false;
27969         this.adapter.setElementSize(this, size);
27970         this.animate = oldAnimate;
27971     },
27972     
27973     /**
27974      * Destroy this splitbar. 
27975      * @param {Boolean} removeEl True to remove the element
27976      */
27977     destroy : function(removeEl){
27978         if(this.shim){
27979             this.shim.remove();
27980         }
27981         this.dd.unreg();
27982         this.proxy.parentNode.removeChild(this.proxy);
27983         if(removeEl){
27984             this.el.remove();
27985         }
27986     }
27987 });
27988
27989 /**
27990  * @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.
27991  */
27992 Roo.SplitBar.createProxy = function(dir){
27993     var proxy = new Roo.Element(document.createElement("div"));
27994     proxy.unselectable();
27995     var cls = 'x-splitbar-proxy';
27996     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27997     document.body.appendChild(proxy.dom);
27998     return proxy.dom;
27999 };
28000
28001 /** 
28002  * @class Roo.SplitBar.BasicLayoutAdapter
28003  * Default Adapter. It assumes the splitter and resizing element are not positioned
28004  * elements and only gets/sets the width of the element. Generally used for table based layouts.
28005  */
28006 Roo.SplitBar.BasicLayoutAdapter = function(){
28007 };
28008
28009 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28010     // do nothing for now
28011     init : function(s){
28012     
28013     },
28014     /**
28015      * Called before drag operations to get the current size of the resizing element. 
28016      * @param {Roo.SplitBar} s The SplitBar using this adapter
28017      */
28018      getElementSize : function(s){
28019         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28020             return s.resizingEl.getWidth();
28021         }else{
28022             return s.resizingEl.getHeight();
28023         }
28024     },
28025     
28026     /**
28027      * Called after drag operations to set the size of the resizing element.
28028      * @param {Roo.SplitBar} s The SplitBar using this adapter
28029      * @param {Number} newSize The new size to set
28030      * @param {Function} onComplete A function to be invoked when resizing is complete
28031      */
28032     setElementSize : function(s, newSize, onComplete){
28033         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28034             if(!s.animate){
28035                 s.resizingEl.setWidth(newSize);
28036                 if(onComplete){
28037                     onComplete(s, newSize);
28038                 }
28039             }else{
28040                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28041             }
28042         }else{
28043             
28044             if(!s.animate){
28045                 s.resizingEl.setHeight(newSize);
28046                 if(onComplete){
28047                     onComplete(s, newSize);
28048                 }
28049             }else{
28050                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28051             }
28052         }
28053     }
28054 };
28055
28056 /** 
28057  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28058  * @extends Roo.SplitBar.BasicLayoutAdapter
28059  * Adapter that  moves the splitter element to align with the resized sizing element. 
28060  * Used with an absolute positioned SplitBar.
28061  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28062  * document.body, make sure you assign an id to the body element.
28063  */
28064 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28065     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28066     this.container = Roo.get(container);
28067 };
28068
28069 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28070     init : function(s){
28071         this.basic.init(s);
28072     },
28073     
28074     getElementSize : function(s){
28075         return this.basic.getElementSize(s);
28076     },
28077     
28078     setElementSize : function(s, newSize, onComplete){
28079         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28080     },
28081     
28082     moveSplitter : function(s){
28083         var yes = Roo.SplitBar;
28084         switch(s.placement){
28085             case yes.LEFT:
28086                 s.el.setX(s.resizingEl.getRight());
28087                 break;
28088             case yes.RIGHT:
28089                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28090                 break;
28091             case yes.TOP:
28092                 s.el.setY(s.resizingEl.getBottom());
28093                 break;
28094             case yes.BOTTOM:
28095                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28096                 break;
28097         }
28098     }
28099 };
28100
28101 /**
28102  * Orientation constant - Create a vertical SplitBar
28103  * @static
28104  * @type Number
28105  */
28106 Roo.SplitBar.VERTICAL = 1;
28107
28108 /**
28109  * Orientation constant - Create a horizontal SplitBar
28110  * @static
28111  * @type Number
28112  */
28113 Roo.SplitBar.HORIZONTAL = 2;
28114
28115 /**
28116  * Placement constant - The resizing element is to the left of the splitter element
28117  * @static
28118  * @type Number
28119  */
28120 Roo.SplitBar.LEFT = 1;
28121
28122 /**
28123  * Placement constant - The resizing element is to the right of the splitter element
28124  * @static
28125  * @type Number
28126  */
28127 Roo.SplitBar.RIGHT = 2;
28128
28129 /**
28130  * Placement constant - The resizing element is positioned above the splitter element
28131  * @static
28132  * @type Number
28133  */
28134 Roo.SplitBar.TOP = 3;
28135
28136 /**
28137  * Placement constant - The resizing element is positioned under splitter element
28138  * @static
28139  * @type Number
28140  */
28141 Roo.SplitBar.BOTTOM = 4;
28142 /*
28143  * Based on:
28144  * Ext JS Library 1.1.1
28145  * Copyright(c) 2006-2007, Ext JS, LLC.
28146  *
28147  * Originally Released Under LGPL - original licence link has changed is not relivant.
28148  *
28149  * Fork - LGPL
28150  * <script type="text/javascript">
28151  */
28152
28153 /**
28154  * @class Roo.View
28155  * @extends Roo.util.Observable
28156  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28157  * This class also supports single and multi selection modes. <br>
28158  * Create a data model bound view:
28159  <pre><code>
28160  var store = new Roo.data.Store(...);
28161
28162  var view = new Roo.View({
28163     el : "my-element",
28164     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28165  
28166     singleSelect: true,
28167     selectedClass: "ydataview-selected",
28168     store: store
28169  });
28170
28171  // listen for node click?
28172  view.on("click", function(vw, index, node, e){
28173  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28174  });
28175
28176  // load XML data
28177  dataModel.load("foobar.xml");
28178  </code></pre>
28179  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28180  * <br><br>
28181  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28182  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28183  * 
28184  * Note: old style constructor is still suported (container, template, config)
28185  * 
28186  * @constructor
28187  * Create a new View
28188  * @param {Object} config The config object
28189  * 
28190  */
28191 Roo.View = function(config, depreciated_tpl, depreciated_config){
28192     
28193     this.parent = false;
28194     
28195     if (typeof(depreciated_tpl) == 'undefined') {
28196         // new way.. - universal constructor.
28197         Roo.apply(this, config);
28198         this.el  = Roo.get(this.el);
28199     } else {
28200         // old format..
28201         this.el  = Roo.get(config);
28202         this.tpl = depreciated_tpl;
28203         Roo.apply(this, depreciated_config);
28204     }
28205     this.wrapEl  = this.el.wrap().wrap();
28206     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28207     
28208     
28209     if(typeof(this.tpl) == "string"){
28210         this.tpl = new Roo.Template(this.tpl);
28211     } else {
28212         // support xtype ctors..
28213         this.tpl = new Roo.factory(this.tpl, Roo);
28214     }
28215     
28216     
28217     this.tpl.compile();
28218     
28219     /** @private */
28220     this.addEvents({
28221         /**
28222          * @event beforeclick
28223          * Fires before a click is processed. Returns false to cancel the default action.
28224          * @param {Roo.View} this
28225          * @param {Number} index The index of the target node
28226          * @param {HTMLElement} node The target node
28227          * @param {Roo.EventObject} e The raw event object
28228          */
28229             "beforeclick" : true,
28230         /**
28231          * @event click
28232          * Fires when a template node is clicked.
28233          * @param {Roo.View} this
28234          * @param {Number} index The index of the target node
28235          * @param {HTMLElement} node The target node
28236          * @param {Roo.EventObject} e The raw event object
28237          */
28238             "click" : true,
28239         /**
28240          * @event dblclick
28241          * Fires when a template node is double clicked.
28242          * @param {Roo.View} this
28243          * @param {Number} index The index of the target node
28244          * @param {HTMLElement} node The target node
28245          * @param {Roo.EventObject} e The raw event object
28246          */
28247             "dblclick" : true,
28248         /**
28249          * @event contextmenu
28250          * Fires when a template node is right clicked.
28251          * @param {Roo.View} this
28252          * @param {Number} index The index of the target node
28253          * @param {HTMLElement} node The target node
28254          * @param {Roo.EventObject} e The raw event object
28255          */
28256             "contextmenu" : true,
28257         /**
28258          * @event selectionchange
28259          * Fires when the selected nodes change.
28260          * @param {Roo.View} this
28261          * @param {Array} selections Array of the selected nodes
28262          */
28263             "selectionchange" : true,
28264     
28265         /**
28266          * @event beforeselect
28267          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28268          * @param {Roo.View} this
28269          * @param {HTMLElement} node The node to be selected
28270          * @param {Array} selections Array of currently selected nodes
28271          */
28272             "beforeselect" : true,
28273         /**
28274          * @event preparedata
28275          * Fires on every row to render, to allow you to change the data.
28276          * @param {Roo.View} this
28277          * @param {Object} data to be rendered (change this)
28278          */
28279           "preparedata" : true
28280           
28281           
28282         });
28283
28284
28285
28286     this.el.on({
28287         "click": this.onClick,
28288         "dblclick": this.onDblClick,
28289         "contextmenu": this.onContextMenu,
28290         scope:this
28291     });
28292
28293     this.selections = [];
28294     this.nodes = [];
28295     this.cmp = new Roo.CompositeElementLite([]);
28296     if(this.store){
28297         this.store = Roo.factory(this.store, Roo.data);
28298         this.setStore(this.store, true);
28299     }
28300     
28301     if ( this.footer && this.footer.xtype) {
28302            
28303          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28304         
28305         this.footer.dataSource = this.store;
28306         this.footer.container = fctr;
28307         this.footer = Roo.factory(this.footer, Roo);
28308         fctr.insertFirst(this.el);
28309         
28310         // this is a bit insane - as the paging toolbar seems to detach the el..
28311 //        dom.parentNode.parentNode.parentNode
28312          // they get detached?
28313     }
28314     
28315     
28316     Roo.View.superclass.constructor.call(this);
28317     
28318     
28319 };
28320
28321 Roo.extend(Roo.View, Roo.util.Observable, {
28322     
28323      /**
28324      * @cfg {Roo.data.Store} store Data store to load data from.
28325      */
28326     store : false,
28327     
28328     /**
28329      * @cfg {String|Roo.Element} el The container element.
28330      */
28331     el : '',
28332     
28333     /**
28334      * @cfg {String|Roo.Template} tpl The template used by this View 
28335      */
28336     tpl : false,
28337     /**
28338      * @cfg {String} dataName the named area of the template to use as the data area
28339      *                          Works with domtemplates roo-name="name"
28340      */
28341     dataName: false,
28342     /**
28343      * @cfg {String} selectedClass The css class to add to selected nodes
28344      */
28345     selectedClass : "x-view-selected",
28346      /**
28347      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28348      */
28349     emptyText : "",
28350     
28351     /**
28352      * @cfg {String} text to display on mask (default Loading)
28353      */
28354     mask : false,
28355     /**
28356      * @cfg {Boolean} multiSelect Allow multiple selection
28357      */
28358     multiSelect : false,
28359     /**
28360      * @cfg {Boolean} singleSelect Allow single selection
28361      */
28362     singleSelect:  false,
28363     
28364     /**
28365      * @cfg {Boolean} toggleSelect - selecting 
28366      */
28367     toggleSelect : false,
28368     
28369     /**
28370      * @cfg {Boolean} tickable - selecting 
28371      */
28372     tickable : false,
28373     
28374     /**
28375      * Returns the element this view is bound to.
28376      * @return {Roo.Element}
28377      */
28378     getEl : function(){
28379         return this.wrapEl;
28380     },
28381     
28382     
28383
28384     /**
28385      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28386      */
28387     refresh : function(){
28388         //Roo.log('refresh');
28389         var t = this.tpl;
28390         
28391         // if we are using something like 'domtemplate', then
28392         // the what gets used is:
28393         // t.applySubtemplate(NAME, data, wrapping data..)
28394         // the outer template then get' applied with
28395         //     the store 'extra data'
28396         // and the body get's added to the
28397         //      roo-name="data" node?
28398         //      <span class='roo-tpl-{name}'></span> ?????
28399         
28400         
28401         
28402         this.clearSelections();
28403         this.el.update("");
28404         var html = [];
28405         var records = this.store.getRange();
28406         if(records.length < 1) {
28407             
28408             // is this valid??  = should it render a template??
28409             
28410             this.el.update(this.emptyText);
28411             return;
28412         }
28413         var el = this.el;
28414         if (this.dataName) {
28415             this.el.update(t.apply(this.store.meta)); //????
28416             el = this.el.child('.roo-tpl-' + this.dataName);
28417         }
28418         
28419         for(var i = 0, len = records.length; i < len; i++){
28420             var data = this.prepareData(records[i].data, i, records[i]);
28421             this.fireEvent("preparedata", this, data, i, records[i]);
28422             
28423             var d = Roo.apply({}, data);
28424             
28425             if(this.tickable){
28426                 Roo.apply(d, {'roo-id' : Roo.id()});
28427                 
28428                 var _this = this;
28429             
28430                 Roo.each(this.parent.item, function(item){
28431                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28432                         return;
28433                     }
28434                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28435                 });
28436             }
28437             
28438             html[html.length] = Roo.util.Format.trim(
28439                 this.dataName ?
28440                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28441                     t.apply(d)
28442             );
28443         }
28444         
28445         
28446         
28447         el.update(html.join(""));
28448         this.nodes = el.dom.childNodes;
28449         this.updateIndexes(0);
28450     },
28451     
28452
28453     /**
28454      * Function to override to reformat the data that is sent to
28455      * the template for each node.
28456      * DEPRICATED - use the preparedata event handler.
28457      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28458      * a JSON object for an UpdateManager bound view).
28459      */
28460     prepareData : function(data, index, record)
28461     {
28462         this.fireEvent("preparedata", this, data, index, record);
28463         return data;
28464     },
28465
28466     onUpdate : function(ds, record){
28467         // Roo.log('on update');   
28468         this.clearSelections();
28469         var index = this.store.indexOf(record);
28470         var n = this.nodes[index];
28471         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28472         n.parentNode.removeChild(n);
28473         this.updateIndexes(index, index);
28474     },
28475
28476     
28477     
28478 // --------- FIXME     
28479     onAdd : function(ds, records, index)
28480     {
28481         //Roo.log(['on Add', ds, records, index] );        
28482         this.clearSelections();
28483         if(this.nodes.length == 0){
28484             this.refresh();
28485             return;
28486         }
28487         var n = this.nodes[index];
28488         for(var i = 0, len = records.length; i < len; i++){
28489             var d = this.prepareData(records[i].data, i, records[i]);
28490             if(n){
28491                 this.tpl.insertBefore(n, d);
28492             }else{
28493                 
28494                 this.tpl.append(this.el, d);
28495             }
28496         }
28497         this.updateIndexes(index);
28498     },
28499
28500     onRemove : function(ds, record, index){
28501        // Roo.log('onRemove');
28502         this.clearSelections();
28503         var el = this.dataName  ?
28504             this.el.child('.roo-tpl-' + this.dataName) :
28505             this.el; 
28506         
28507         el.dom.removeChild(this.nodes[index]);
28508         this.updateIndexes(index);
28509     },
28510
28511     /**
28512      * Refresh an individual node.
28513      * @param {Number} index
28514      */
28515     refreshNode : function(index){
28516         this.onUpdate(this.store, this.store.getAt(index));
28517     },
28518
28519     updateIndexes : function(startIndex, endIndex){
28520         var ns = this.nodes;
28521         startIndex = startIndex || 0;
28522         endIndex = endIndex || ns.length - 1;
28523         for(var i = startIndex; i <= endIndex; i++){
28524             ns[i].nodeIndex = i;
28525         }
28526     },
28527
28528     /**
28529      * Changes the data store this view uses and refresh the view.
28530      * @param {Store} store
28531      */
28532     setStore : function(store, initial){
28533         if(!initial && this.store){
28534             this.store.un("datachanged", this.refresh);
28535             this.store.un("add", this.onAdd);
28536             this.store.un("remove", this.onRemove);
28537             this.store.un("update", this.onUpdate);
28538             this.store.un("clear", this.refresh);
28539             this.store.un("beforeload", this.onBeforeLoad);
28540             this.store.un("load", this.onLoad);
28541             this.store.un("loadexception", this.onLoad);
28542         }
28543         if(store){
28544           
28545             store.on("datachanged", this.refresh, this);
28546             store.on("add", this.onAdd, this);
28547             store.on("remove", this.onRemove, this);
28548             store.on("update", this.onUpdate, this);
28549             store.on("clear", this.refresh, this);
28550             store.on("beforeload", this.onBeforeLoad, this);
28551             store.on("load", this.onLoad, this);
28552             store.on("loadexception", this.onLoad, this);
28553         }
28554         
28555         if(store){
28556             this.refresh();
28557         }
28558     },
28559     /**
28560      * onbeforeLoad - masks the loading area.
28561      *
28562      */
28563     onBeforeLoad : function(store,opts)
28564     {
28565          //Roo.log('onBeforeLoad');   
28566         if (!opts.add) {
28567             this.el.update("");
28568         }
28569         this.el.mask(this.mask ? this.mask : "Loading" ); 
28570     },
28571     onLoad : function ()
28572     {
28573         this.el.unmask();
28574     },
28575     
28576
28577     /**
28578      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28579      * @param {HTMLElement} node
28580      * @return {HTMLElement} The template node
28581      */
28582     findItemFromChild : function(node){
28583         var el = this.dataName  ?
28584             this.el.child('.roo-tpl-' + this.dataName,true) :
28585             this.el.dom; 
28586         
28587         if(!node || node.parentNode == el){
28588                     return node;
28589             }
28590             var p = node.parentNode;
28591             while(p && p != el){
28592             if(p.parentNode == el){
28593                 return p;
28594             }
28595             p = p.parentNode;
28596         }
28597             return null;
28598     },
28599
28600     /** @ignore */
28601     onClick : function(e){
28602         var item = this.findItemFromChild(e.getTarget());
28603         if(item){
28604             var index = this.indexOf(item);
28605             if(this.onItemClick(item, index, e) !== false){
28606                 this.fireEvent("click", this, index, item, e);
28607             }
28608         }else{
28609             this.clearSelections();
28610         }
28611     },
28612
28613     /** @ignore */
28614     onContextMenu : function(e){
28615         var item = this.findItemFromChild(e.getTarget());
28616         if(item){
28617             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28618         }
28619     },
28620
28621     /** @ignore */
28622     onDblClick : function(e){
28623         var item = this.findItemFromChild(e.getTarget());
28624         if(item){
28625             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28626         }
28627     },
28628
28629     onItemClick : function(item, index, e)
28630     {
28631         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28632             return false;
28633         }
28634         if (this.toggleSelect) {
28635             var m = this.isSelected(item) ? 'unselect' : 'select';
28636             //Roo.log(m);
28637             var _t = this;
28638             _t[m](item, true, false);
28639             return true;
28640         }
28641         if(this.multiSelect || this.singleSelect){
28642             if(this.multiSelect && e.shiftKey && this.lastSelection){
28643                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28644             }else{
28645                 this.select(item, this.multiSelect && e.ctrlKey);
28646                 this.lastSelection = item;
28647             }
28648             
28649             if(!this.tickable){
28650                 e.preventDefault();
28651             }
28652             
28653         }
28654         return true;
28655     },
28656
28657     /**
28658      * Get the number of selected nodes.
28659      * @return {Number}
28660      */
28661     getSelectionCount : function(){
28662         return this.selections.length;
28663     },
28664
28665     /**
28666      * Get the currently selected nodes.
28667      * @return {Array} An array of HTMLElements
28668      */
28669     getSelectedNodes : function(){
28670         return this.selections;
28671     },
28672
28673     /**
28674      * Get the indexes of the selected nodes.
28675      * @return {Array}
28676      */
28677     getSelectedIndexes : function(){
28678         var indexes = [], s = this.selections;
28679         for(var i = 0, len = s.length; i < len; i++){
28680             indexes.push(s[i].nodeIndex);
28681         }
28682         return indexes;
28683     },
28684
28685     /**
28686      * Clear all selections
28687      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28688      */
28689     clearSelections : function(suppressEvent){
28690         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28691             this.cmp.elements = this.selections;
28692             this.cmp.removeClass(this.selectedClass);
28693             this.selections = [];
28694             if(!suppressEvent){
28695                 this.fireEvent("selectionchange", this, this.selections);
28696             }
28697         }
28698     },
28699
28700     /**
28701      * Returns true if the passed node is selected
28702      * @param {HTMLElement/Number} node The node or node index
28703      * @return {Boolean}
28704      */
28705     isSelected : function(node){
28706         var s = this.selections;
28707         if(s.length < 1){
28708             return false;
28709         }
28710         node = this.getNode(node);
28711         return s.indexOf(node) !== -1;
28712     },
28713
28714     /**
28715      * Selects nodes.
28716      * @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
28717      * @param {Boolean} keepExisting (optional) true to keep existing selections
28718      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28719      */
28720     select : function(nodeInfo, keepExisting, suppressEvent){
28721         if(nodeInfo instanceof Array){
28722             if(!keepExisting){
28723                 this.clearSelections(true);
28724             }
28725             for(var i = 0, len = nodeInfo.length; i < len; i++){
28726                 this.select(nodeInfo[i], true, true);
28727             }
28728             return;
28729         } 
28730         var node = this.getNode(nodeInfo);
28731         if(!node || this.isSelected(node)){
28732             return; // already selected.
28733         }
28734         if(!keepExisting){
28735             this.clearSelections(true);
28736         }
28737         
28738         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28739             Roo.fly(node).addClass(this.selectedClass);
28740             this.selections.push(node);
28741             if(!suppressEvent){
28742                 this.fireEvent("selectionchange", this, this.selections);
28743             }
28744         }
28745         
28746         
28747     },
28748       /**
28749      * Unselects nodes.
28750      * @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
28751      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28752      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28753      */
28754     unselect : function(nodeInfo, keepExisting, suppressEvent)
28755     {
28756         if(nodeInfo instanceof Array){
28757             Roo.each(this.selections, function(s) {
28758                 this.unselect(s, nodeInfo);
28759             }, this);
28760             return;
28761         }
28762         var node = this.getNode(nodeInfo);
28763         if(!node || !this.isSelected(node)){
28764             //Roo.log("not selected");
28765             return; // not selected.
28766         }
28767         // fireevent???
28768         var ns = [];
28769         Roo.each(this.selections, function(s) {
28770             if (s == node ) {
28771                 Roo.fly(node).removeClass(this.selectedClass);
28772
28773                 return;
28774             }
28775             ns.push(s);
28776         },this);
28777         
28778         this.selections= ns;
28779         this.fireEvent("selectionchange", this, this.selections);
28780     },
28781
28782     /**
28783      * Gets a template node.
28784      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28785      * @return {HTMLElement} The node or null if it wasn't found
28786      */
28787     getNode : function(nodeInfo){
28788         if(typeof nodeInfo == "string"){
28789             return document.getElementById(nodeInfo);
28790         }else if(typeof nodeInfo == "number"){
28791             return this.nodes[nodeInfo];
28792         }
28793         return nodeInfo;
28794     },
28795
28796     /**
28797      * Gets a range template nodes.
28798      * @param {Number} startIndex
28799      * @param {Number} endIndex
28800      * @return {Array} An array of nodes
28801      */
28802     getNodes : function(start, end){
28803         var ns = this.nodes;
28804         start = start || 0;
28805         end = typeof end == "undefined" ? ns.length - 1 : end;
28806         var nodes = [];
28807         if(start <= end){
28808             for(var i = start; i <= end; i++){
28809                 nodes.push(ns[i]);
28810             }
28811         } else{
28812             for(var i = start; i >= end; i--){
28813                 nodes.push(ns[i]);
28814             }
28815         }
28816         return nodes;
28817     },
28818
28819     /**
28820      * Finds the index of the passed node
28821      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28822      * @return {Number} The index of the node or -1
28823      */
28824     indexOf : function(node){
28825         node = this.getNode(node);
28826         if(typeof node.nodeIndex == "number"){
28827             return node.nodeIndex;
28828         }
28829         var ns = this.nodes;
28830         for(var i = 0, len = ns.length; i < len; i++){
28831             if(ns[i] == node){
28832                 return i;
28833             }
28834         }
28835         return -1;
28836     }
28837 });
28838 /*
28839  * Based on:
28840  * Ext JS Library 1.1.1
28841  * Copyright(c) 2006-2007, Ext JS, LLC.
28842  *
28843  * Originally Released Under LGPL - original licence link has changed is not relivant.
28844  *
28845  * Fork - LGPL
28846  * <script type="text/javascript">
28847  */
28848
28849 /**
28850  * @class Roo.JsonView
28851  * @extends Roo.View
28852  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28853 <pre><code>
28854 var view = new Roo.JsonView({
28855     container: "my-element",
28856     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28857     multiSelect: true, 
28858     jsonRoot: "data" 
28859 });
28860
28861 // listen for node click?
28862 view.on("click", function(vw, index, node, e){
28863     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28864 });
28865
28866 // direct load of JSON data
28867 view.load("foobar.php");
28868
28869 // Example from my blog list
28870 var tpl = new Roo.Template(
28871     '&lt;div class="entry"&gt;' +
28872     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28873     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28874     "&lt;/div&gt;&lt;hr /&gt;"
28875 );
28876
28877 var moreView = new Roo.JsonView({
28878     container :  "entry-list", 
28879     template : tpl,
28880     jsonRoot: "posts"
28881 });
28882 moreView.on("beforerender", this.sortEntries, this);
28883 moreView.load({
28884     url: "/blog/get-posts.php",
28885     params: "allposts=true",
28886     text: "Loading Blog Entries..."
28887 });
28888 </code></pre>
28889
28890 * Note: old code is supported with arguments : (container, template, config)
28891
28892
28893  * @constructor
28894  * Create a new JsonView
28895  * 
28896  * @param {Object} config The config object
28897  * 
28898  */
28899 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28900     
28901     
28902     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28903
28904     var um = this.el.getUpdateManager();
28905     um.setRenderer(this);
28906     um.on("update", this.onLoad, this);
28907     um.on("failure", this.onLoadException, this);
28908
28909     /**
28910      * @event beforerender
28911      * Fires before rendering of the downloaded JSON data.
28912      * @param {Roo.JsonView} this
28913      * @param {Object} data The JSON data loaded
28914      */
28915     /**
28916      * @event load
28917      * Fires when data is loaded.
28918      * @param {Roo.JsonView} this
28919      * @param {Object} data The JSON data loaded
28920      * @param {Object} response The raw Connect response object
28921      */
28922     /**
28923      * @event loadexception
28924      * Fires when loading fails.
28925      * @param {Roo.JsonView} this
28926      * @param {Object} response The raw Connect response object
28927      */
28928     this.addEvents({
28929         'beforerender' : true,
28930         'load' : true,
28931         'loadexception' : true
28932     });
28933 };
28934 Roo.extend(Roo.JsonView, Roo.View, {
28935     /**
28936      * @type {String} The root property in the loaded JSON object that contains the data
28937      */
28938     jsonRoot : "",
28939
28940     /**
28941      * Refreshes the view.
28942      */
28943     refresh : function(){
28944         this.clearSelections();
28945         this.el.update("");
28946         var html = [];
28947         var o = this.jsonData;
28948         if(o && o.length > 0){
28949             for(var i = 0, len = o.length; i < len; i++){
28950                 var data = this.prepareData(o[i], i, o);
28951                 html[html.length] = this.tpl.apply(data);
28952             }
28953         }else{
28954             html.push(this.emptyText);
28955         }
28956         this.el.update(html.join(""));
28957         this.nodes = this.el.dom.childNodes;
28958         this.updateIndexes(0);
28959     },
28960
28961     /**
28962      * 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.
28963      * @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:
28964      <pre><code>
28965      view.load({
28966          url: "your-url.php",
28967          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28968          callback: yourFunction,
28969          scope: yourObject, //(optional scope)
28970          discardUrl: false,
28971          nocache: false,
28972          text: "Loading...",
28973          timeout: 30,
28974          scripts: false
28975      });
28976      </code></pre>
28977      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28978      * 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.
28979      * @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}
28980      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28981      * @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.
28982      */
28983     load : function(){
28984         var um = this.el.getUpdateManager();
28985         um.update.apply(um, arguments);
28986     },
28987
28988     // note - render is a standard framework call...
28989     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28990     render : function(el, response){
28991         
28992         this.clearSelections();
28993         this.el.update("");
28994         var o;
28995         try{
28996             if (response != '') {
28997                 o = Roo.util.JSON.decode(response.responseText);
28998                 if(this.jsonRoot){
28999                     
29000                     o = o[this.jsonRoot];
29001                 }
29002             }
29003         } catch(e){
29004         }
29005         /**
29006          * The current JSON data or null
29007          */
29008         this.jsonData = o;
29009         this.beforeRender();
29010         this.refresh();
29011     },
29012
29013 /**
29014  * Get the number of records in the current JSON dataset
29015  * @return {Number}
29016  */
29017     getCount : function(){
29018         return this.jsonData ? this.jsonData.length : 0;
29019     },
29020
29021 /**
29022  * Returns the JSON object for the specified node(s)
29023  * @param {HTMLElement/Array} node The node or an array of nodes
29024  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29025  * you get the JSON object for the node
29026  */
29027     getNodeData : function(node){
29028         if(node instanceof Array){
29029             var data = [];
29030             for(var i = 0, len = node.length; i < len; i++){
29031                 data.push(this.getNodeData(node[i]));
29032             }
29033             return data;
29034         }
29035         return this.jsonData[this.indexOf(node)] || null;
29036     },
29037
29038     beforeRender : function(){
29039         this.snapshot = this.jsonData;
29040         if(this.sortInfo){
29041             this.sort.apply(this, this.sortInfo);
29042         }
29043         this.fireEvent("beforerender", this, this.jsonData);
29044     },
29045
29046     onLoad : function(el, o){
29047         this.fireEvent("load", this, this.jsonData, o);
29048     },
29049
29050     onLoadException : function(el, o){
29051         this.fireEvent("loadexception", this, o);
29052     },
29053
29054 /**
29055  * Filter the data by a specific property.
29056  * @param {String} property A property on your JSON objects
29057  * @param {String/RegExp} value Either string that the property values
29058  * should start with, or a RegExp to test against the property
29059  */
29060     filter : function(property, value){
29061         if(this.jsonData){
29062             var data = [];
29063             var ss = this.snapshot;
29064             if(typeof value == "string"){
29065                 var vlen = value.length;
29066                 if(vlen == 0){
29067                     this.clearFilter();
29068                     return;
29069                 }
29070                 value = value.toLowerCase();
29071                 for(var i = 0, len = ss.length; i < len; i++){
29072                     var o = ss[i];
29073                     if(o[property].substr(0, vlen).toLowerCase() == value){
29074                         data.push(o);
29075                     }
29076                 }
29077             } else if(value.exec){ // regex?
29078                 for(var i = 0, len = ss.length; i < len; i++){
29079                     var o = ss[i];
29080                     if(value.test(o[property])){
29081                         data.push(o);
29082                     }
29083                 }
29084             } else{
29085                 return;
29086             }
29087             this.jsonData = data;
29088             this.refresh();
29089         }
29090     },
29091
29092 /**
29093  * Filter by a function. The passed function will be called with each
29094  * object in the current dataset. If the function returns true the value is kept,
29095  * otherwise it is filtered.
29096  * @param {Function} fn
29097  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29098  */
29099     filterBy : function(fn, scope){
29100         if(this.jsonData){
29101             var data = [];
29102             var ss = this.snapshot;
29103             for(var i = 0, len = ss.length; i < len; i++){
29104                 var o = ss[i];
29105                 if(fn.call(scope || this, o)){
29106                     data.push(o);
29107                 }
29108             }
29109             this.jsonData = data;
29110             this.refresh();
29111         }
29112     },
29113
29114 /**
29115  * Clears the current filter.
29116  */
29117     clearFilter : function(){
29118         if(this.snapshot && this.jsonData != this.snapshot){
29119             this.jsonData = this.snapshot;
29120             this.refresh();
29121         }
29122     },
29123
29124
29125 /**
29126  * Sorts the data for this view and refreshes it.
29127  * @param {String} property A property on your JSON objects to sort on
29128  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29129  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29130  */
29131     sort : function(property, dir, sortType){
29132         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29133         if(this.jsonData){
29134             var p = property;
29135             var dsc = dir && dir.toLowerCase() == "desc";
29136             var f = function(o1, o2){
29137                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29138                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29139                 ;
29140                 if(v1 < v2){
29141                     return dsc ? +1 : -1;
29142                 } else if(v1 > v2){
29143                     return dsc ? -1 : +1;
29144                 } else{
29145                     return 0;
29146                 }
29147             };
29148             this.jsonData.sort(f);
29149             this.refresh();
29150             if(this.jsonData != this.snapshot){
29151                 this.snapshot.sort(f);
29152             }
29153         }
29154     }
29155 });/*
29156  * Based on:
29157  * Ext JS Library 1.1.1
29158  * Copyright(c) 2006-2007, Ext JS, LLC.
29159  *
29160  * Originally Released Under LGPL - original licence link has changed is not relivant.
29161  *
29162  * Fork - LGPL
29163  * <script type="text/javascript">
29164  */
29165  
29166
29167 /**
29168  * @class Roo.ColorPalette
29169  * @extends Roo.Component
29170  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29171  * Here's an example of typical usage:
29172  * <pre><code>
29173 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29174 cp.render('my-div');
29175
29176 cp.on('select', function(palette, selColor){
29177     // do something with selColor
29178 });
29179 </code></pre>
29180  * @constructor
29181  * Create a new ColorPalette
29182  * @param {Object} config The config object
29183  */
29184 Roo.ColorPalette = function(config){
29185     Roo.ColorPalette.superclass.constructor.call(this, config);
29186     this.addEvents({
29187         /**
29188              * @event select
29189              * Fires when a color is selected
29190              * @param {ColorPalette} this
29191              * @param {String} color The 6-digit color hex code (without the # symbol)
29192              */
29193         select: true
29194     });
29195
29196     if(this.handler){
29197         this.on("select", this.handler, this.scope, true);
29198     }
29199 };
29200 Roo.extend(Roo.ColorPalette, Roo.Component, {
29201     /**
29202      * @cfg {String} itemCls
29203      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29204      */
29205     itemCls : "x-color-palette",
29206     /**
29207      * @cfg {String} value
29208      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29209      * the hex codes are case-sensitive.
29210      */
29211     value : null,
29212     clickEvent:'click',
29213     // private
29214     ctype: "Roo.ColorPalette",
29215
29216     /**
29217      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29218      */
29219     allowReselect : false,
29220
29221     /**
29222      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29223      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29224      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29225      * of colors with the width setting until the box is symmetrical.</p>
29226      * <p>You can override individual colors if needed:</p>
29227      * <pre><code>
29228 var cp = new Roo.ColorPalette();
29229 cp.colors[0] = "FF0000";  // change the first box to red
29230 </code></pre>
29231
29232 Or you can provide a custom array of your own for complete control:
29233 <pre><code>
29234 var cp = new Roo.ColorPalette();
29235 cp.colors = ["000000", "993300", "333300"];
29236 </code></pre>
29237      * @type Array
29238      */
29239     colors : [
29240         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29241         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29242         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29243         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29244         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29245     ],
29246
29247     // private
29248     onRender : function(container, position){
29249         var t = new Roo.MasterTemplate(
29250             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29251         );
29252         var c = this.colors;
29253         for(var i = 0, len = c.length; i < len; i++){
29254             t.add([c[i]]);
29255         }
29256         var el = document.createElement("div");
29257         el.className = this.itemCls;
29258         t.overwrite(el);
29259         container.dom.insertBefore(el, position);
29260         this.el = Roo.get(el);
29261         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29262         if(this.clickEvent != 'click'){
29263             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29264         }
29265     },
29266
29267     // private
29268     afterRender : function(){
29269         Roo.ColorPalette.superclass.afterRender.call(this);
29270         if(this.value){
29271             var s = this.value;
29272             this.value = null;
29273             this.select(s);
29274         }
29275     },
29276
29277     // private
29278     handleClick : function(e, t){
29279         e.preventDefault();
29280         if(!this.disabled){
29281             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29282             this.select(c.toUpperCase());
29283         }
29284     },
29285
29286     /**
29287      * Selects the specified color in the palette (fires the select event)
29288      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29289      */
29290     select : function(color){
29291         color = color.replace("#", "");
29292         if(color != this.value || this.allowReselect){
29293             var el = this.el;
29294             if(this.value){
29295                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29296             }
29297             el.child("a.color-"+color).addClass("x-color-palette-sel");
29298             this.value = color;
29299             this.fireEvent("select", this, color);
29300         }
29301     }
29302 });/*
29303  * Based on:
29304  * Ext JS Library 1.1.1
29305  * Copyright(c) 2006-2007, Ext JS, LLC.
29306  *
29307  * Originally Released Under LGPL - original licence link has changed is not relivant.
29308  *
29309  * Fork - LGPL
29310  * <script type="text/javascript">
29311  */
29312  
29313 /**
29314  * @class Roo.DatePicker
29315  * @extends Roo.Component
29316  * Simple date picker class.
29317  * @constructor
29318  * Create a new DatePicker
29319  * @param {Object} config The config object
29320  */
29321 Roo.DatePicker = function(config){
29322     Roo.DatePicker.superclass.constructor.call(this, config);
29323
29324     this.value = config && config.value ?
29325                  config.value.clearTime() : new Date().clearTime();
29326
29327     this.addEvents({
29328         /**
29329              * @event select
29330              * Fires when a date is selected
29331              * @param {DatePicker} this
29332              * @param {Date} date The selected date
29333              */
29334         'select': true,
29335         /**
29336              * @event monthchange
29337              * Fires when the displayed month changes 
29338              * @param {DatePicker} this
29339              * @param {Date} date The selected month
29340              */
29341         'monthchange': true
29342     });
29343
29344     if(this.handler){
29345         this.on("select", this.handler,  this.scope || this);
29346     }
29347     // build the disabledDatesRE
29348     if(!this.disabledDatesRE && this.disabledDates){
29349         var dd = this.disabledDates;
29350         var re = "(?:";
29351         for(var i = 0; i < dd.length; i++){
29352             re += dd[i];
29353             if(i != dd.length-1) {
29354                 re += "|";
29355             }
29356         }
29357         this.disabledDatesRE = new RegExp(re + ")");
29358     }
29359 };
29360
29361 Roo.extend(Roo.DatePicker, Roo.Component, {
29362     /**
29363      * @cfg {String} todayText
29364      * The text to display on the button that selects the current date (defaults to "Today")
29365      */
29366     todayText : "Today",
29367     /**
29368      * @cfg {String} okText
29369      * The text to display on the ok button
29370      */
29371     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29372     /**
29373      * @cfg {String} cancelText
29374      * The text to display on the cancel button
29375      */
29376     cancelText : "Cancel",
29377     /**
29378      * @cfg {String} todayTip
29379      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29380      */
29381     todayTip : "{0} (Spacebar)",
29382     /**
29383      * @cfg {Date} minDate
29384      * Minimum allowable date (JavaScript date object, defaults to null)
29385      */
29386     minDate : null,
29387     /**
29388      * @cfg {Date} maxDate
29389      * Maximum allowable date (JavaScript date object, defaults to null)
29390      */
29391     maxDate : null,
29392     /**
29393      * @cfg {String} minText
29394      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29395      */
29396     minText : "This date is before the minimum date",
29397     /**
29398      * @cfg {String} maxText
29399      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29400      */
29401     maxText : "This date is after the maximum date",
29402     /**
29403      * @cfg {String} format
29404      * The default date format string which can be overriden for localization support.  The format must be
29405      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29406      */
29407     format : "m/d/y",
29408     /**
29409      * @cfg {Array} disabledDays
29410      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29411      */
29412     disabledDays : null,
29413     /**
29414      * @cfg {String} disabledDaysText
29415      * The tooltip to display when the date falls on a disabled day (defaults to "")
29416      */
29417     disabledDaysText : "",
29418     /**
29419      * @cfg {RegExp} disabledDatesRE
29420      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29421      */
29422     disabledDatesRE : null,
29423     /**
29424      * @cfg {String} disabledDatesText
29425      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29426      */
29427     disabledDatesText : "",
29428     /**
29429      * @cfg {Boolean} constrainToViewport
29430      * True to constrain the date picker to the viewport (defaults to true)
29431      */
29432     constrainToViewport : true,
29433     /**
29434      * @cfg {Array} monthNames
29435      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29436      */
29437     monthNames : Date.monthNames,
29438     /**
29439      * @cfg {Array} dayNames
29440      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29441      */
29442     dayNames : Date.dayNames,
29443     /**
29444      * @cfg {String} nextText
29445      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29446      */
29447     nextText: 'Next Month (Control+Right)',
29448     /**
29449      * @cfg {String} prevText
29450      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29451      */
29452     prevText: 'Previous Month (Control+Left)',
29453     /**
29454      * @cfg {String} monthYearText
29455      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29456      */
29457     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29458     /**
29459      * @cfg {Number} startDay
29460      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29461      */
29462     startDay : 0,
29463     /**
29464      * @cfg {Bool} showClear
29465      * Show a clear button (usefull for date form elements that can be blank.)
29466      */
29467     
29468     showClear: false,
29469     
29470     /**
29471      * Sets the value of the date field
29472      * @param {Date} value The date to set
29473      */
29474     setValue : function(value){
29475         var old = this.value;
29476         
29477         if (typeof(value) == 'string') {
29478          
29479             value = Date.parseDate(value, this.format);
29480         }
29481         if (!value) {
29482             value = new Date();
29483         }
29484         
29485         this.value = value.clearTime(true);
29486         if(this.el){
29487             this.update(this.value);
29488         }
29489     },
29490
29491     /**
29492      * Gets the current selected value of the date field
29493      * @return {Date} The selected date
29494      */
29495     getValue : function(){
29496         return this.value;
29497     },
29498
29499     // private
29500     focus : function(){
29501         if(this.el){
29502             this.update(this.activeDate);
29503         }
29504     },
29505
29506     // privateval
29507     onRender : function(container, position){
29508         
29509         var m = [
29510              '<table cellspacing="0">',
29511                 '<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>',
29512                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29513         var dn = this.dayNames;
29514         for(var i = 0; i < 7; i++){
29515             var d = this.startDay+i;
29516             if(d > 6){
29517                 d = d-7;
29518             }
29519             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29520         }
29521         m[m.length] = "</tr></thead><tbody><tr>";
29522         for(var i = 0; i < 42; i++) {
29523             if(i % 7 == 0 && i != 0){
29524                 m[m.length] = "</tr><tr>";
29525             }
29526             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29527         }
29528         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29529             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29530
29531         var el = document.createElement("div");
29532         el.className = "x-date-picker";
29533         el.innerHTML = m.join("");
29534
29535         container.dom.insertBefore(el, position);
29536
29537         this.el = Roo.get(el);
29538         this.eventEl = Roo.get(el.firstChild);
29539
29540         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29541             handler: this.showPrevMonth,
29542             scope: this,
29543             preventDefault:true,
29544             stopDefault:true
29545         });
29546
29547         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29548             handler: this.showNextMonth,
29549             scope: this,
29550             preventDefault:true,
29551             stopDefault:true
29552         });
29553
29554         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29555
29556         this.monthPicker = this.el.down('div.x-date-mp');
29557         this.monthPicker.enableDisplayMode('block');
29558         
29559         var kn = new Roo.KeyNav(this.eventEl, {
29560             "left" : function(e){
29561                 e.ctrlKey ?
29562                     this.showPrevMonth() :
29563                     this.update(this.activeDate.add("d", -1));
29564             },
29565
29566             "right" : function(e){
29567                 e.ctrlKey ?
29568                     this.showNextMonth() :
29569                     this.update(this.activeDate.add("d", 1));
29570             },
29571
29572             "up" : function(e){
29573                 e.ctrlKey ?
29574                     this.showNextYear() :
29575                     this.update(this.activeDate.add("d", -7));
29576             },
29577
29578             "down" : function(e){
29579                 e.ctrlKey ?
29580                     this.showPrevYear() :
29581                     this.update(this.activeDate.add("d", 7));
29582             },
29583
29584             "pageUp" : function(e){
29585                 this.showNextMonth();
29586             },
29587
29588             "pageDown" : function(e){
29589                 this.showPrevMonth();
29590             },
29591
29592             "enter" : function(e){
29593                 e.stopPropagation();
29594                 return true;
29595             },
29596
29597             scope : this
29598         });
29599
29600         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29601
29602         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29603
29604         this.el.unselectable();
29605         
29606         this.cells = this.el.select("table.x-date-inner tbody td");
29607         this.textNodes = this.el.query("table.x-date-inner tbody span");
29608
29609         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29610             text: "&#160;",
29611             tooltip: this.monthYearText
29612         });
29613
29614         this.mbtn.on('click', this.showMonthPicker, this);
29615         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29616
29617
29618         var today = (new Date()).dateFormat(this.format);
29619         
29620         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29621         if (this.showClear) {
29622             baseTb.add( new Roo.Toolbar.Fill());
29623         }
29624         baseTb.add({
29625             text: String.format(this.todayText, today),
29626             tooltip: String.format(this.todayTip, today),
29627             handler: this.selectToday,
29628             scope: this
29629         });
29630         
29631         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29632             
29633         //});
29634         if (this.showClear) {
29635             
29636             baseTb.add( new Roo.Toolbar.Fill());
29637             baseTb.add({
29638                 text: '&#160;',
29639                 cls: 'x-btn-icon x-btn-clear',
29640                 handler: function() {
29641                     //this.value = '';
29642                     this.fireEvent("select", this, '');
29643                 },
29644                 scope: this
29645             });
29646         }
29647         
29648         
29649         if(Roo.isIE){
29650             this.el.repaint();
29651         }
29652         this.update(this.value);
29653     },
29654
29655     createMonthPicker : function(){
29656         if(!this.monthPicker.dom.firstChild){
29657             var buf = ['<table border="0" cellspacing="0">'];
29658             for(var i = 0; i < 6; i++){
29659                 buf.push(
29660                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29661                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29662                     i == 0 ?
29663                     '<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>' :
29664                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29665                 );
29666             }
29667             buf.push(
29668                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29669                     this.okText,
29670                     '</button><button type="button" class="x-date-mp-cancel">',
29671                     this.cancelText,
29672                     '</button></td></tr>',
29673                 '</table>'
29674             );
29675             this.monthPicker.update(buf.join(''));
29676             this.monthPicker.on('click', this.onMonthClick, this);
29677             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29678
29679             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29680             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29681
29682             this.mpMonths.each(function(m, a, i){
29683                 i += 1;
29684                 if((i%2) == 0){
29685                     m.dom.xmonth = 5 + Math.round(i * .5);
29686                 }else{
29687                     m.dom.xmonth = Math.round((i-1) * .5);
29688                 }
29689             });
29690         }
29691     },
29692
29693     showMonthPicker : function(){
29694         this.createMonthPicker();
29695         var size = this.el.getSize();
29696         this.monthPicker.setSize(size);
29697         this.monthPicker.child('table').setSize(size);
29698
29699         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29700         this.updateMPMonth(this.mpSelMonth);
29701         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29702         this.updateMPYear(this.mpSelYear);
29703
29704         this.monthPicker.slideIn('t', {duration:.2});
29705     },
29706
29707     updateMPYear : function(y){
29708         this.mpyear = y;
29709         var ys = this.mpYears.elements;
29710         for(var i = 1; i <= 10; i++){
29711             var td = ys[i-1], y2;
29712             if((i%2) == 0){
29713                 y2 = y + Math.round(i * .5);
29714                 td.firstChild.innerHTML = y2;
29715                 td.xyear = y2;
29716             }else{
29717                 y2 = y - (5-Math.round(i * .5));
29718                 td.firstChild.innerHTML = y2;
29719                 td.xyear = y2;
29720             }
29721             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29722         }
29723     },
29724
29725     updateMPMonth : function(sm){
29726         this.mpMonths.each(function(m, a, i){
29727             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29728         });
29729     },
29730
29731     selectMPMonth: function(m){
29732         
29733     },
29734
29735     onMonthClick : function(e, t){
29736         e.stopEvent();
29737         var el = new Roo.Element(t), pn;
29738         if(el.is('button.x-date-mp-cancel')){
29739             this.hideMonthPicker();
29740         }
29741         else if(el.is('button.x-date-mp-ok')){
29742             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29743             this.hideMonthPicker();
29744         }
29745         else if(pn = el.up('td.x-date-mp-month', 2)){
29746             this.mpMonths.removeClass('x-date-mp-sel');
29747             pn.addClass('x-date-mp-sel');
29748             this.mpSelMonth = pn.dom.xmonth;
29749         }
29750         else if(pn = el.up('td.x-date-mp-year', 2)){
29751             this.mpYears.removeClass('x-date-mp-sel');
29752             pn.addClass('x-date-mp-sel');
29753             this.mpSelYear = pn.dom.xyear;
29754         }
29755         else if(el.is('a.x-date-mp-prev')){
29756             this.updateMPYear(this.mpyear-10);
29757         }
29758         else if(el.is('a.x-date-mp-next')){
29759             this.updateMPYear(this.mpyear+10);
29760         }
29761     },
29762
29763     onMonthDblClick : function(e, t){
29764         e.stopEvent();
29765         var el = new Roo.Element(t), pn;
29766         if(pn = el.up('td.x-date-mp-month', 2)){
29767             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29768             this.hideMonthPicker();
29769         }
29770         else if(pn = el.up('td.x-date-mp-year', 2)){
29771             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29772             this.hideMonthPicker();
29773         }
29774     },
29775
29776     hideMonthPicker : function(disableAnim){
29777         if(this.monthPicker){
29778             if(disableAnim === true){
29779                 this.monthPicker.hide();
29780             }else{
29781                 this.monthPicker.slideOut('t', {duration:.2});
29782             }
29783         }
29784     },
29785
29786     // private
29787     showPrevMonth : function(e){
29788         this.update(this.activeDate.add("mo", -1));
29789     },
29790
29791     // private
29792     showNextMonth : function(e){
29793         this.update(this.activeDate.add("mo", 1));
29794     },
29795
29796     // private
29797     showPrevYear : function(){
29798         this.update(this.activeDate.add("y", -1));
29799     },
29800
29801     // private
29802     showNextYear : function(){
29803         this.update(this.activeDate.add("y", 1));
29804     },
29805
29806     // private
29807     handleMouseWheel : function(e){
29808         var delta = e.getWheelDelta();
29809         if(delta > 0){
29810             this.showPrevMonth();
29811             e.stopEvent();
29812         } else if(delta < 0){
29813             this.showNextMonth();
29814             e.stopEvent();
29815         }
29816     },
29817
29818     // private
29819     handleDateClick : function(e, t){
29820         e.stopEvent();
29821         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29822             this.setValue(new Date(t.dateValue));
29823             this.fireEvent("select", this, this.value);
29824         }
29825     },
29826
29827     // private
29828     selectToday : function(){
29829         this.setValue(new Date().clearTime());
29830         this.fireEvent("select", this, this.value);
29831     },
29832
29833     // private
29834     update : function(date)
29835     {
29836         var vd = this.activeDate;
29837         this.activeDate = date;
29838         if(vd && this.el){
29839             var t = date.getTime();
29840             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29841                 this.cells.removeClass("x-date-selected");
29842                 this.cells.each(function(c){
29843                    if(c.dom.firstChild.dateValue == t){
29844                        c.addClass("x-date-selected");
29845                        setTimeout(function(){
29846                             try{c.dom.firstChild.focus();}catch(e){}
29847                        }, 50);
29848                        return false;
29849                    }
29850                 });
29851                 return;
29852             }
29853         }
29854         
29855         var days = date.getDaysInMonth();
29856         var firstOfMonth = date.getFirstDateOfMonth();
29857         var startingPos = firstOfMonth.getDay()-this.startDay;
29858
29859         if(startingPos <= this.startDay){
29860             startingPos += 7;
29861         }
29862
29863         var pm = date.add("mo", -1);
29864         var prevStart = pm.getDaysInMonth()-startingPos;
29865
29866         var cells = this.cells.elements;
29867         var textEls = this.textNodes;
29868         days += startingPos;
29869
29870         // convert everything to numbers so it's fast
29871         var day = 86400000;
29872         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29873         var today = new Date().clearTime().getTime();
29874         var sel = date.clearTime().getTime();
29875         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29876         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29877         var ddMatch = this.disabledDatesRE;
29878         var ddText = this.disabledDatesText;
29879         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29880         var ddaysText = this.disabledDaysText;
29881         var format = this.format;
29882
29883         var setCellClass = function(cal, cell){
29884             cell.title = "";
29885             var t = d.getTime();
29886             cell.firstChild.dateValue = t;
29887             if(t == today){
29888                 cell.className += " x-date-today";
29889                 cell.title = cal.todayText;
29890             }
29891             if(t == sel){
29892                 cell.className += " x-date-selected";
29893                 setTimeout(function(){
29894                     try{cell.firstChild.focus();}catch(e){}
29895                 }, 50);
29896             }
29897             // disabling
29898             if(t < min) {
29899                 cell.className = " x-date-disabled";
29900                 cell.title = cal.minText;
29901                 return;
29902             }
29903             if(t > max) {
29904                 cell.className = " x-date-disabled";
29905                 cell.title = cal.maxText;
29906                 return;
29907             }
29908             if(ddays){
29909                 if(ddays.indexOf(d.getDay()) != -1){
29910                     cell.title = ddaysText;
29911                     cell.className = " x-date-disabled";
29912                 }
29913             }
29914             if(ddMatch && format){
29915                 var fvalue = d.dateFormat(format);
29916                 if(ddMatch.test(fvalue)){
29917                     cell.title = ddText.replace("%0", fvalue);
29918                     cell.className = " x-date-disabled";
29919                 }
29920             }
29921         };
29922
29923         var i = 0;
29924         for(; i < startingPos; i++) {
29925             textEls[i].innerHTML = (++prevStart);
29926             d.setDate(d.getDate()+1);
29927             cells[i].className = "x-date-prevday";
29928             setCellClass(this, cells[i]);
29929         }
29930         for(; i < days; i++){
29931             intDay = i - startingPos + 1;
29932             textEls[i].innerHTML = (intDay);
29933             d.setDate(d.getDate()+1);
29934             cells[i].className = "x-date-active";
29935             setCellClass(this, cells[i]);
29936         }
29937         var extraDays = 0;
29938         for(; i < 42; i++) {
29939              textEls[i].innerHTML = (++extraDays);
29940              d.setDate(d.getDate()+1);
29941              cells[i].className = "x-date-nextday";
29942              setCellClass(this, cells[i]);
29943         }
29944
29945         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29946         this.fireEvent('monthchange', this, date);
29947         
29948         if(!this.internalRender){
29949             var main = this.el.dom.firstChild;
29950             var w = main.offsetWidth;
29951             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29952             Roo.fly(main).setWidth(w);
29953             this.internalRender = true;
29954             // opera does not respect the auto grow header center column
29955             // then, after it gets a width opera refuses to recalculate
29956             // without a second pass
29957             if(Roo.isOpera && !this.secondPass){
29958                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29959                 this.secondPass = true;
29960                 this.update.defer(10, this, [date]);
29961             }
29962         }
29963         
29964         
29965     }
29966 });        /*
29967  * Based on:
29968  * Ext JS Library 1.1.1
29969  * Copyright(c) 2006-2007, Ext JS, LLC.
29970  *
29971  * Originally Released Under LGPL - original licence link has changed is not relivant.
29972  *
29973  * Fork - LGPL
29974  * <script type="text/javascript">
29975  */
29976 /**
29977  * @class Roo.TabPanel
29978  * @extends Roo.util.Observable
29979  * A lightweight tab container.
29980  * <br><br>
29981  * Usage:
29982  * <pre><code>
29983 // basic tabs 1, built from existing content
29984 var tabs = new Roo.TabPanel("tabs1");
29985 tabs.addTab("script", "View Script");
29986 tabs.addTab("markup", "View Markup");
29987 tabs.activate("script");
29988
29989 // more advanced tabs, built from javascript
29990 var jtabs = new Roo.TabPanel("jtabs");
29991 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29992
29993 // set up the UpdateManager
29994 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29995 var updater = tab2.getUpdateManager();
29996 updater.setDefaultUrl("ajax1.htm");
29997 tab2.on('activate', updater.refresh, updater, true);
29998
29999 // Use setUrl for Ajax loading
30000 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
30001 tab3.setUrl("ajax2.htm", null, true);
30002
30003 // Disabled tab
30004 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
30005 tab4.disable();
30006
30007 jtabs.activate("jtabs-1");
30008  * </code></pre>
30009  * @constructor
30010  * Create a new TabPanel.
30011  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30012  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30013  */
30014 Roo.TabPanel = function(container, config){
30015     /**
30016     * The container element for this TabPanel.
30017     * @type Roo.Element
30018     */
30019     this.el = Roo.get(container, true);
30020     if(config){
30021         if(typeof config == "boolean"){
30022             this.tabPosition = config ? "bottom" : "top";
30023         }else{
30024             Roo.apply(this, config);
30025         }
30026     }
30027     if(this.tabPosition == "bottom"){
30028         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30029         this.el.addClass("x-tabs-bottom");
30030     }
30031     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30032     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30033     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30034     if(Roo.isIE){
30035         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30036     }
30037     if(this.tabPosition != "bottom"){
30038         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30039          * @type Roo.Element
30040          */
30041         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30042         this.el.addClass("x-tabs-top");
30043     }
30044     this.items = [];
30045
30046     this.bodyEl.setStyle("position", "relative");
30047
30048     this.active = null;
30049     this.activateDelegate = this.activate.createDelegate(this);
30050
30051     this.addEvents({
30052         /**
30053          * @event tabchange
30054          * Fires when the active tab changes
30055          * @param {Roo.TabPanel} this
30056          * @param {Roo.TabPanelItem} activePanel The new active tab
30057          */
30058         "tabchange": true,
30059         /**
30060          * @event beforetabchange
30061          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30062          * @param {Roo.TabPanel} this
30063          * @param {Object} e Set cancel to true on this object to cancel the tab change
30064          * @param {Roo.TabPanelItem} tab The tab being changed to
30065          */
30066         "beforetabchange" : true
30067     });
30068
30069     Roo.EventManager.onWindowResize(this.onResize, this);
30070     this.cpad = this.el.getPadding("lr");
30071     this.hiddenCount = 0;
30072
30073
30074     // toolbar on the tabbar support...
30075     if (this.toolbar) {
30076         var tcfg = this.toolbar;
30077         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
30078         this.toolbar = new Roo.Toolbar(tcfg);
30079         if (Roo.isSafari) {
30080             var tbl = tcfg.container.child('table', true);
30081             tbl.setAttribute('width', '100%');
30082         }
30083         
30084     }
30085    
30086
30087
30088     Roo.TabPanel.superclass.constructor.call(this);
30089 };
30090
30091 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30092     /*
30093      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30094      */
30095     tabPosition : "top",
30096     /*
30097      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30098      */
30099     currentTabWidth : 0,
30100     /*
30101      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30102      */
30103     minTabWidth : 40,
30104     /*
30105      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30106      */
30107     maxTabWidth : 250,
30108     /*
30109      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30110      */
30111     preferredTabWidth : 175,
30112     /*
30113      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30114      */
30115     resizeTabs : false,
30116     /*
30117      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30118      */
30119     monitorResize : true,
30120     /*
30121      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
30122      */
30123     toolbar : false,
30124
30125     /**
30126      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30127      * @param {String} id The id of the div to use <b>or create</b>
30128      * @param {String} text The text for the tab
30129      * @param {String} content (optional) Content to put in the TabPanelItem body
30130      * @param {Boolean} closable (optional) True to create a close icon on the tab
30131      * @return {Roo.TabPanelItem} The created TabPanelItem
30132      */
30133     addTab : function(id, text, content, closable){
30134         var item = new Roo.TabPanelItem(this, id, text, closable);
30135         this.addTabItem(item);
30136         if(content){
30137             item.setContent(content);
30138         }
30139         return item;
30140     },
30141
30142     /**
30143      * Returns the {@link Roo.TabPanelItem} with the specified id/index
30144      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30145      * @return {Roo.TabPanelItem}
30146      */
30147     getTab : function(id){
30148         return this.items[id];
30149     },
30150
30151     /**
30152      * Hides the {@link Roo.TabPanelItem} with the specified id/index
30153      * @param {String/Number} id The id or index of the TabPanelItem to hide.
30154      */
30155     hideTab : function(id){
30156         var t = this.items[id];
30157         if(!t.isHidden()){
30158            t.setHidden(true);
30159            this.hiddenCount++;
30160            this.autoSizeTabs();
30161         }
30162     },
30163
30164     /**
30165      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30166      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30167      */
30168     unhideTab : function(id){
30169         var t = this.items[id];
30170         if(t.isHidden()){
30171            t.setHidden(false);
30172            this.hiddenCount--;
30173            this.autoSizeTabs();
30174         }
30175     },
30176
30177     /**
30178      * Adds an existing {@link Roo.TabPanelItem}.
30179      * @param {Roo.TabPanelItem} item The TabPanelItem to add
30180      */
30181     addTabItem : function(item){
30182         this.items[item.id] = item;
30183         this.items.push(item);
30184         if(this.resizeTabs){
30185            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30186            this.autoSizeTabs();
30187         }else{
30188             item.autoSize();
30189         }
30190     },
30191
30192     /**
30193      * Removes a {@link Roo.TabPanelItem}.
30194      * @param {String/Number} id The id or index of the TabPanelItem to remove.
30195      */
30196     removeTab : function(id){
30197         var items = this.items;
30198         var tab = items[id];
30199         if(!tab) { return; }
30200         var index = items.indexOf(tab);
30201         if(this.active == tab && items.length > 1){
30202             var newTab = this.getNextAvailable(index);
30203             if(newTab) {
30204                 newTab.activate();
30205             }
30206         }
30207         this.stripEl.dom.removeChild(tab.pnode.dom);
30208         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30209             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30210         }
30211         items.splice(index, 1);
30212         delete this.items[tab.id];
30213         tab.fireEvent("close", tab);
30214         tab.purgeListeners();
30215         this.autoSizeTabs();
30216     },
30217
30218     getNextAvailable : function(start){
30219         var items = this.items;
30220         var index = start;
30221         // look for a next tab that will slide over to
30222         // replace the one being removed
30223         while(index < items.length){
30224             var item = items[++index];
30225             if(item && !item.isHidden()){
30226                 return item;
30227             }
30228         }
30229         // if one isn't found select the previous tab (on the left)
30230         index = start;
30231         while(index >= 0){
30232             var item = items[--index];
30233             if(item && !item.isHidden()){
30234                 return item;
30235             }
30236         }
30237         return null;
30238     },
30239
30240     /**
30241      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30242      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30243      */
30244     disableTab : function(id){
30245         var tab = this.items[id];
30246         if(tab && this.active != tab){
30247             tab.disable();
30248         }
30249     },
30250
30251     /**
30252      * Enables a {@link Roo.TabPanelItem} that is disabled.
30253      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30254      */
30255     enableTab : function(id){
30256         var tab = this.items[id];
30257         tab.enable();
30258     },
30259
30260     /**
30261      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30262      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30263      * @return {Roo.TabPanelItem} The TabPanelItem.
30264      */
30265     activate : function(id){
30266         var tab = this.items[id];
30267         if(!tab){
30268             return null;
30269         }
30270         if(tab == this.active || tab.disabled){
30271             return tab;
30272         }
30273         var e = {};
30274         this.fireEvent("beforetabchange", this, e, tab);
30275         if(e.cancel !== true && !tab.disabled){
30276             if(this.active){
30277                 this.active.hide();
30278             }
30279             this.active = this.items[id];
30280             this.active.show();
30281             this.fireEvent("tabchange", this, this.active);
30282         }
30283         return tab;
30284     },
30285
30286     /**
30287      * Gets the active {@link Roo.TabPanelItem}.
30288      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30289      */
30290     getActiveTab : function(){
30291         return this.active;
30292     },
30293
30294     /**
30295      * Updates the tab body element to fit the height of the container element
30296      * for overflow scrolling
30297      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30298      */
30299     syncHeight : function(targetHeight){
30300         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30301         var bm = this.bodyEl.getMargins();
30302         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30303         this.bodyEl.setHeight(newHeight);
30304         return newHeight;
30305     },
30306
30307     onResize : function(){
30308         if(this.monitorResize){
30309             this.autoSizeTabs();
30310         }
30311     },
30312
30313     /**
30314      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30315      */
30316     beginUpdate : function(){
30317         this.updating = true;
30318     },
30319
30320     /**
30321      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30322      */
30323     endUpdate : function(){
30324         this.updating = false;
30325         this.autoSizeTabs();
30326     },
30327
30328     /**
30329      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30330      */
30331     autoSizeTabs : function(){
30332         var count = this.items.length;
30333         var vcount = count - this.hiddenCount;
30334         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30335             return;
30336         }
30337         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30338         var availWidth = Math.floor(w / vcount);
30339         var b = this.stripBody;
30340         if(b.getWidth() > w){
30341             var tabs = this.items;
30342             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30343             if(availWidth < this.minTabWidth){
30344                 /*if(!this.sleft){    // incomplete scrolling code
30345                     this.createScrollButtons();
30346                 }
30347                 this.showScroll();
30348                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30349             }
30350         }else{
30351             if(this.currentTabWidth < this.preferredTabWidth){
30352                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30353             }
30354         }
30355     },
30356
30357     /**
30358      * Returns the number of tabs in this TabPanel.
30359      * @return {Number}
30360      */
30361      getCount : function(){
30362          return this.items.length;
30363      },
30364
30365     /**
30366      * Resizes all the tabs to the passed width
30367      * @param {Number} The new width
30368      */
30369     setTabWidth : function(width){
30370         this.currentTabWidth = width;
30371         for(var i = 0, len = this.items.length; i < len; i++) {
30372                 if(!this.items[i].isHidden()) {
30373                 this.items[i].setWidth(width);
30374             }
30375         }
30376     },
30377
30378     /**
30379      * Destroys this TabPanel
30380      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30381      */
30382     destroy : function(removeEl){
30383         Roo.EventManager.removeResizeListener(this.onResize, this);
30384         for(var i = 0, len = this.items.length; i < len; i++){
30385             this.items[i].purgeListeners();
30386         }
30387         if(removeEl === true){
30388             this.el.update("");
30389             this.el.remove();
30390         }
30391     }
30392 });
30393
30394 /**
30395  * @class Roo.TabPanelItem
30396  * @extends Roo.util.Observable
30397  * Represents an individual item (tab plus body) in a TabPanel.
30398  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30399  * @param {String} id The id of this TabPanelItem
30400  * @param {String} text The text for the tab of this TabPanelItem
30401  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30402  */
30403 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30404     /**
30405      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30406      * @type Roo.TabPanel
30407      */
30408     this.tabPanel = tabPanel;
30409     /**
30410      * The id for this TabPanelItem
30411      * @type String
30412      */
30413     this.id = id;
30414     /** @private */
30415     this.disabled = false;
30416     /** @private */
30417     this.text = text;
30418     /** @private */
30419     this.loaded = false;
30420     this.closable = closable;
30421
30422     /**
30423      * The body element for this TabPanelItem.
30424      * @type Roo.Element
30425      */
30426     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30427     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30428     this.bodyEl.setStyle("display", "block");
30429     this.bodyEl.setStyle("zoom", "1");
30430     this.hideAction();
30431
30432     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30433     /** @private */
30434     this.el = Roo.get(els.el, true);
30435     this.inner = Roo.get(els.inner, true);
30436     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30437     this.pnode = Roo.get(els.el.parentNode, true);
30438     this.el.on("mousedown", this.onTabMouseDown, this);
30439     this.el.on("click", this.onTabClick, this);
30440     /** @private */
30441     if(closable){
30442         var c = Roo.get(els.close, true);
30443         c.dom.title = this.closeText;
30444         c.addClassOnOver("close-over");
30445         c.on("click", this.closeClick, this);
30446      }
30447
30448     this.addEvents({
30449          /**
30450          * @event activate
30451          * Fires when this tab becomes the active tab.
30452          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30453          * @param {Roo.TabPanelItem} this
30454          */
30455         "activate": true,
30456         /**
30457          * @event beforeclose
30458          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30459          * @param {Roo.TabPanelItem} this
30460          * @param {Object} e Set cancel to true on this object to cancel the close.
30461          */
30462         "beforeclose": true,
30463         /**
30464          * @event close
30465          * Fires when this tab is closed.
30466          * @param {Roo.TabPanelItem} this
30467          */
30468          "close": true,
30469         /**
30470          * @event deactivate
30471          * Fires when this tab is no longer the active tab.
30472          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30473          * @param {Roo.TabPanelItem} this
30474          */
30475          "deactivate" : true
30476     });
30477     this.hidden = false;
30478
30479     Roo.TabPanelItem.superclass.constructor.call(this);
30480 };
30481
30482 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30483     purgeListeners : function(){
30484        Roo.util.Observable.prototype.purgeListeners.call(this);
30485        this.el.removeAllListeners();
30486     },
30487     /**
30488      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30489      */
30490     show : function(){
30491         this.pnode.addClass("on");
30492         this.showAction();
30493         if(Roo.isOpera){
30494             this.tabPanel.stripWrap.repaint();
30495         }
30496         this.fireEvent("activate", this.tabPanel, this);
30497     },
30498
30499     /**
30500      * Returns true if this tab is the active tab.
30501      * @return {Boolean}
30502      */
30503     isActive : function(){
30504         return this.tabPanel.getActiveTab() == this;
30505     },
30506
30507     /**
30508      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30509      */
30510     hide : function(){
30511         this.pnode.removeClass("on");
30512         this.hideAction();
30513         this.fireEvent("deactivate", this.tabPanel, this);
30514     },
30515
30516     hideAction : function(){
30517         this.bodyEl.hide();
30518         this.bodyEl.setStyle("position", "absolute");
30519         this.bodyEl.setLeft("-20000px");
30520         this.bodyEl.setTop("-20000px");
30521     },
30522
30523     showAction : function(){
30524         this.bodyEl.setStyle("position", "relative");
30525         this.bodyEl.setTop("");
30526         this.bodyEl.setLeft("");
30527         this.bodyEl.show();
30528     },
30529
30530     /**
30531      * Set the tooltip for the tab.
30532      * @param {String} tooltip The tab's tooltip
30533      */
30534     setTooltip : function(text){
30535         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30536             this.textEl.dom.qtip = text;
30537             this.textEl.dom.removeAttribute('title');
30538         }else{
30539             this.textEl.dom.title = text;
30540         }
30541     },
30542
30543     onTabClick : function(e){
30544         e.preventDefault();
30545         this.tabPanel.activate(this.id);
30546     },
30547
30548     onTabMouseDown : function(e){
30549         e.preventDefault();
30550         this.tabPanel.activate(this.id);
30551     },
30552
30553     getWidth : function(){
30554         return this.inner.getWidth();
30555     },
30556
30557     setWidth : function(width){
30558         var iwidth = width - this.pnode.getPadding("lr");
30559         this.inner.setWidth(iwidth);
30560         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30561         this.pnode.setWidth(width);
30562     },
30563
30564     /**
30565      * Show or hide the tab
30566      * @param {Boolean} hidden True to hide or false to show.
30567      */
30568     setHidden : function(hidden){
30569         this.hidden = hidden;
30570         this.pnode.setStyle("display", hidden ? "none" : "");
30571     },
30572
30573     /**
30574      * Returns true if this tab is "hidden"
30575      * @return {Boolean}
30576      */
30577     isHidden : function(){
30578         return this.hidden;
30579     },
30580
30581     /**
30582      * Returns the text for this tab
30583      * @return {String}
30584      */
30585     getText : function(){
30586         return this.text;
30587     },
30588
30589     autoSize : function(){
30590         //this.el.beginMeasure();
30591         this.textEl.setWidth(1);
30592         /*
30593          *  #2804 [new] Tabs in Roojs
30594          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30595          */
30596         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30597         //this.el.endMeasure();
30598     },
30599
30600     /**
30601      * Sets the text for the tab (Note: this also sets the tooltip text)
30602      * @param {String} text The tab's text and tooltip
30603      */
30604     setText : function(text){
30605         this.text = text;
30606         this.textEl.update(text);
30607         this.setTooltip(text);
30608         if(!this.tabPanel.resizeTabs){
30609             this.autoSize();
30610         }
30611     },
30612     /**
30613      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30614      */
30615     activate : function(){
30616         this.tabPanel.activate(this.id);
30617     },
30618
30619     /**
30620      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30621      */
30622     disable : function(){
30623         if(this.tabPanel.active != this){
30624             this.disabled = true;
30625             this.pnode.addClass("disabled");
30626         }
30627     },
30628
30629     /**
30630      * Enables this TabPanelItem if it was previously disabled.
30631      */
30632     enable : function(){
30633         this.disabled = false;
30634         this.pnode.removeClass("disabled");
30635     },
30636
30637     /**
30638      * Sets the content for this TabPanelItem.
30639      * @param {String} content The content
30640      * @param {Boolean} loadScripts true to look for and load scripts
30641      */
30642     setContent : function(content, loadScripts){
30643         this.bodyEl.update(content, loadScripts);
30644     },
30645
30646     /**
30647      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30648      * @return {Roo.UpdateManager} The UpdateManager
30649      */
30650     getUpdateManager : function(){
30651         return this.bodyEl.getUpdateManager();
30652     },
30653
30654     /**
30655      * Set a URL to be used to load the content for this TabPanelItem.
30656      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30657      * @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)
30658      * @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)
30659      * @return {Roo.UpdateManager} The UpdateManager
30660      */
30661     setUrl : function(url, params, loadOnce){
30662         if(this.refreshDelegate){
30663             this.un('activate', this.refreshDelegate);
30664         }
30665         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30666         this.on("activate", this.refreshDelegate);
30667         return this.bodyEl.getUpdateManager();
30668     },
30669
30670     /** @private */
30671     _handleRefresh : function(url, params, loadOnce){
30672         if(!loadOnce || !this.loaded){
30673             var updater = this.bodyEl.getUpdateManager();
30674             updater.update(url, params, this._setLoaded.createDelegate(this));
30675         }
30676     },
30677
30678     /**
30679      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30680      *   Will fail silently if the setUrl method has not been called.
30681      *   This does not activate the panel, just updates its content.
30682      */
30683     refresh : function(){
30684         if(this.refreshDelegate){
30685            this.loaded = false;
30686            this.refreshDelegate();
30687         }
30688     },
30689
30690     /** @private */
30691     _setLoaded : function(){
30692         this.loaded = true;
30693     },
30694
30695     /** @private */
30696     closeClick : function(e){
30697         var o = {};
30698         e.stopEvent();
30699         this.fireEvent("beforeclose", this, o);
30700         if(o.cancel !== true){
30701             this.tabPanel.removeTab(this.id);
30702         }
30703     },
30704     /**
30705      * The text displayed in the tooltip for the close icon.
30706      * @type String
30707      */
30708     closeText : "Close this tab"
30709 });
30710
30711 /** @private */
30712 Roo.TabPanel.prototype.createStrip = function(container){
30713     var strip = document.createElement("div");
30714     strip.className = "x-tabs-wrap";
30715     container.appendChild(strip);
30716     return strip;
30717 };
30718 /** @private */
30719 Roo.TabPanel.prototype.createStripList = function(strip){
30720     // div wrapper for retard IE
30721     // returns the "tr" element.
30722     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30723         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30724         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30725     return strip.firstChild.firstChild.firstChild.firstChild;
30726 };
30727 /** @private */
30728 Roo.TabPanel.prototype.createBody = function(container){
30729     var body = document.createElement("div");
30730     Roo.id(body, "tab-body");
30731     Roo.fly(body).addClass("x-tabs-body");
30732     container.appendChild(body);
30733     return body;
30734 };
30735 /** @private */
30736 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30737     var body = Roo.getDom(id);
30738     if(!body){
30739         body = document.createElement("div");
30740         body.id = id;
30741     }
30742     Roo.fly(body).addClass("x-tabs-item-body");
30743     bodyEl.insertBefore(body, bodyEl.firstChild);
30744     return body;
30745 };
30746 /** @private */
30747 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30748     var td = document.createElement("td");
30749     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30750     //stripEl.appendChild(td);
30751     if(closable){
30752         td.className = "x-tabs-closable";
30753         if(!this.closeTpl){
30754             this.closeTpl = new Roo.Template(
30755                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30756                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30757                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30758             );
30759         }
30760         var el = this.closeTpl.overwrite(td, {"text": text});
30761         var close = el.getElementsByTagName("div")[0];
30762         var inner = el.getElementsByTagName("em")[0];
30763         return {"el": el, "close": close, "inner": inner};
30764     } else {
30765         if(!this.tabTpl){
30766             this.tabTpl = new Roo.Template(
30767                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30768                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30769             );
30770         }
30771         var el = this.tabTpl.overwrite(td, {"text": text});
30772         var inner = el.getElementsByTagName("em")[0];
30773         return {"el": el, "inner": inner};
30774     }
30775 };/*
30776  * Based on:
30777  * Ext JS Library 1.1.1
30778  * Copyright(c) 2006-2007, Ext JS, LLC.
30779  *
30780  * Originally Released Under LGPL - original licence link has changed is not relivant.
30781  *
30782  * Fork - LGPL
30783  * <script type="text/javascript">
30784  */
30785
30786 /**
30787  * @class Roo.Button
30788  * @extends Roo.util.Observable
30789  * Simple Button class
30790  * @cfg {String} text The button text
30791  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30792  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30793  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30794  * @cfg {Object} scope The scope of the handler
30795  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30796  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30797  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30798  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30799  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30800  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30801    applies if enableToggle = true)
30802  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30803  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30804   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30805  * @constructor
30806  * Create a new button
30807  * @param {Object} config The config object
30808  */
30809 Roo.Button = function(renderTo, config)
30810 {
30811     if (!config) {
30812         config = renderTo;
30813         renderTo = config.renderTo || false;
30814     }
30815     
30816     Roo.apply(this, config);
30817     this.addEvents({
30818         /**
30819              * @event click
30820              * Fires when this button is clicked
30821              * @param {Button} this
30822              * @param {EventObject} e The click event
30823              */
30824             "click" : true,
30825         /**
30826              * @event toggle
30827              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30828              * @param {Button} this
30829              * @param {Boolean} pressed
30830              */
30831             "toggle" : true,
30832         /**
30833              * @event mouseover
30834              * Fires when the mouse hovers over the button
30835              * @param {Button} this
30836              * @param {Event} e The event object
30837              */
30838         'mouseover' : true,
30839         /**
30840              * @event mouseout
30841              * Fires when the mouse exits the button
30842              * @param {Button} this
30843              * @param {Event} e The event object
30844              */
30845         'mouseout': true,
30846          /**
30847              * @event render
30848              * Fires when the button is rendered
30849              * @param {Button} this
30850              */
30851         'render': true
30852     });
30853     if(this.menu){
30854         this.menu = Roo.menu.MenuMgr.get(this.menu);
30855     }
30856     // register listeners first!!  - so render can be captured..
30857     Roo.util.Observable.call(this);
30858     if(renderTo){
30859         this.render(renderTo);
30860     }
30861     
30862   
30863 };
30864
30865 Roo.extend(Roo.Button, Roo.util.Observable, {
30866     /**
30867      * 
30868      */
30869     
30870     /**
30871      * Read-only. True if this button is hidden
30872      * @type Boolean
30873      */
30874     hidden : false,
30875     /**
30876      * Read-only. True if this button is disabled
30877      * @type Boolean
30878      */
30879     disabled : false,
30880     /**
30881      * Read-only. True if this button is pressed (only if enableToggle = true)
30882      * @type Boolean
30883      */
30884     pressed : false,
30885
30886     /**
30887      * @cfg {Number} tabIndex 
30888      * The DOM tabIndex for this button (defaults to undefined)
30889      */
30890     tabIndex : undefined,
30891
30892     /**
30893      * @cfg {Boolean} enableToggle
30894      * True to enable pressed/not pressed toggling (defaults to false)
30895      */
30896     enableToggle: false,
30897     /**
30898      * @cfg {Roo.menu.Menu} menu
30899      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30900      */
30901     menu : undefined,
30902     /**
30903      * @cfg {String} menuAlign
30904      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30905      */
30906     menuAlign : "tl-bl?",
30907
30908     /**
30909      * @cfg {String} iconCls
30910      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30911      */
30912     iconCls : undefined,
30913     /**
30914      * @cfg {String} type
30915      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30916      */
30917     type : 'button',
30918
30919     // private
30920     menuClassTarget: 'tr',
30921
30922     /**
30923      * @cfg {String} clickEvent
30924      * The type of event to map to the button's event handler (defaults to 'click')
30925      */
30926     clickEvent : 'click',
30927
30928     /**
30929      * @cfg {Boolean} handleMouseEvents
30930      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30931      */
30932     handleMouseEvents : true,
30933
30934     /**
30935      * @cfg {String} tooltipType
30936      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30937      */
30938     tooltipType : 'qtip',
30939
30940     /**
30941      * @cfg {String} cls
30942      * A CSS class to apply to the button's main element.
30943      */
30944     
30945     /**
30946      * @cfg {Roo.Template} template (Optional)
30947      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30948      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30949      * require code modifications if required elements (e.g. a button) aren't present.
30950      */
30951
30952     // private
30953     render : function(renderTo){
30954         var btn;
30955         if(this.hideParent){
30956             this.parentEl = Roo.get(renderTo);
30957         }
30958         if(!this.dhconfig){
30959             if(!this.template){
30960                 if(!Roo.Button.buttonTemplate){
30961                     // hideous table template
30962                     Roo.Button.buttonTemplate = new Roo.Template(
30963                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30964                         '<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>',
30965                         "</tr></tbody></table>");
30966                 }
30967                 this.template = Roo.Button.buttonTemplate;
30968             }
30969             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30970             var btnEl = btn.child("button:first");
30971             btnEl.on('focus', this.onFocus, this);
30972             btnEl.on('blur', this.onBlur, this);
30973             if(this.cls){
30974                 btn.addClass(this.cls);
30975             }
30976             if(this.icon){
30977                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30978             }
30979             if(this.iconCls){
30980                 btnEl.addClass(this.iconCls);
30981                 if(!this.cls){
30982                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30983                 }
30984             }
30985             if(this.tabIndex !== undefined){
30986                 btnEl.dom.tabIndex = this.tabIndex;
30987             }
30988             if(this.tooltip){
30989                 if(typeof this.tooltip == 'object'){
30990                     Roo.QuickTips.tips(Roo.apply({
30991                           target: btnEl.id
30992                     }, this.tooltip));
30993                 } else {
30994                     btnEl.dom[this.tooltipType] = this.tooltip;
30995                 }
30996             }
30997         }else{
30998             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30999         }
31000         this.el = btn;
31001         if(this.id){
31002             this.el.dom.id = this.el.id = this.id;
31003         }
31004         if(this.menu){
31005             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
31006             this.menu.on("show", this.onMenuShow, this);
31007             this.menu.on("hide", this.onMenuHide, this);
31008         }
31009         btn.addClass("x-btn");
31010         if(Roo.isIE && !Roo.isIE7){
31011             this.autoWidth.defer(1, this);
31012         }else{
31013             this.autoWidth();
31014         }
31015         if(this.handleMouseEvents){
31016             btn.on("mouseover", this.onMouseOver, this);
31017             btn.on("mouseout", this.onMouseOut, this);
31018             btn.on("mousedown", this.onMouseDown, this);
31019         }
31020         btn.on(this.clickEvent, this.onClick, this);
31021         //btn.on("mouseup", this.onMouseUp, this);
31022         if(this.hidden){
31023             this.hide();
31024         }
31025         if(this.disabled){
31026             this.disable();
31027         }
31028         Roo.ButtonToggleMgr.register(this);
31029         if(this.pressed){
31030             this.el.addClass("x-btn-pressed");
31031         }
31032         if(this.repeat){
31033             var repeater = new Roo.util.ClickRepeater(btn,
31034                 typeof this.repeat == "object" ? this.repeat : {}
31035             );
31036             repeater.on("click", this.onClick,  this);
31037         }
31038         
31039         this.fireEvent('render', this);
31040         
31041     },
31042     /**
31043      * Returns the button's underlying element
31044      * @return {Roo.Element} The element
31045      */
31046     getEl : function(){
31047         return this.el;  
31048     },
31049     
31050     /**
31051      * Destroys this Button and removes any listeners.
31052      */
31053     destroy : function(){
31054         Roo.ButtonToggleMgr.unregister(this);
31055         this.el.removeAllListeners();
31056         this.purgeListeners();
31057         this.el.remove();
31058     },
31059
31060     // private
31061     autoWidth : function(){
31062         if(this.el){
31063             this.el.setWidth("auto");
31064             if(Roo.isIE7 && Roo.isStrict){
31065                 var ib = this.el.child('button');
31066                 if(ib && ib.getWidth() > 20){
31067                     ib.clip();
31068                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31069                 }
31070             }
31071             if(this.minWidth){
31072                 if(this.hidden){
31073                     this.el.beginMeasure();
31074                 }
31075                 if(this.el.getWidth() < this.minWidth){
31076                     this.el.setWidth(this.minWidth);
31077                 }
31078                 if(this.hidden){
31079                     this.el.endMeasure();
31080                 }
31081             }
31082         }
31083     },
31084
31085     /**
31086      * Assigns this button's click handler
31087      * @param {Function} handler The function to call when the button is clicked
31088      * @param {Object} scope (optional) Scope for the function passed in
31089      */
31090     setHandler : function(handler, scope){
31091         this.handler = handler;
31092         this.scope = scope;  
31093     },
31094     
31095     /**
31096      * Sets this button's text
31097      * @param {String} text The button text
31098      */
31099     setText : function(text){
31100         this.text = text;
31101         if(this.el){
31102             this.el.child("td.x-btn-center button.x-btn-text").update(text);
31103         }
31104         this.autoWidth();
31105     },
31106     
31107     /**
31108      * Gets the text for this button
31109      * @return {String} The button text
31110      */
31111     getText : function(){
31112         return this.text;  
31113     },
31114     
31115     /**
31116      * Show this button
31117      */
31118     show: function(){
31119         this.hidden = false;
31120         if(this.el){
31121             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31122         }
31123     },
31124     
31125     /**
31126      * Hide this button
31127      */
31128     hide: function(){
31129         this.hidden = true;
31130         if(this.el){
31131             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31132         }
31133     },
31134     
31135     /**
31136      * Convenience function for boolean show/hide
31137      * @param {Boolean} visible True to show, false to hide
31138      */
31139     setVisible: function(visible){
31140         if(visible) {
31141             this.show();
31142         }else{
31143             this.hide();
31144         }
31145     },
31146     /**
31147          * Similar to toggle, but does not trigger event.
31148          * @param {Boolean} state [required] Force a particular state
31149          */
31150         setPressed : function(state)
31151         {
31152             if(state != this.pressed){
31153             if(state){
31154                 this.el.addClass("x-btn-pressed");
31155                 this.pressed = true;
31156             }else{
31157                 this.el.removeClass("x-btn-pressed");
31158                 this.pressed = false;
31159             }
31160         }
31161         },
31162         
31163     /**
31164      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31165      * @param {Boolean} state (optional) Force a particular state
31166      */
31167     toggle : function(state){
31168         state = state === undefined ? !this.pressed : state;
31169         if(state != this.pressed){
31170             if(state){
31171                 this.el.addClass("x-btn-pressed");
31172                 this.pressed = true;
31173                 this.fireEvent("toggle", this, true);
31174             }else{
31175                 this.el.removeClass("x-btn-pressed");
31176                 this.pressed = false;
31177                 this.fireEvent("toggle", this, false);
31178             }
31179             if(this.toggleHandler){
31180                 this.toggleHandler.call(this.scope || this, this, state);
31181             }
31182         }
31183     },
31184     
31185         
31186         
31187     /**
31188      * Focus the button
31189      */
31190     focus : function(){
31191         this.el.child('button:first').focus();
31192     },
31193     
31194     /**
31195      * Disable this button
31196      */
31197     disable : function(){
31198         if(this.el){
31199             this.el.addClass("x-btn-disabled");
31200         }
31201         this.disabled = true;
31202     },
31203     
31204     /**
31205      * Enable this button
31206      */
31207     enable : function(){
31208         if(this.el){
31209             this.el.removeClass("x-btn-disabled");
31210         }
31211         this.disabled = false;
31212     },
31213
31214     /**
31215      * Convenience function for boolean enable/disable
31216      * @param {Boolean} enabled True to enable, false to disable
31217      */
31218     setDisabled : function(v){
31219         this[v !== true ? "enable" : "disable"]();
31220     },
31221
31222     // private
31223     onClick : function(e)
31224     {
31225         if(e){
31226             e.preventDefault();
31227         }
31228         if(e.button != 0){
31229             return;
31230         }
31231         if(!this.disabled){
31232             if(this.enableToggle){
31233                 this.toggle();
31234             }
31235             if(this.menu && !this.menu.isVisible()){
31236                 this.menu.show(this.el, this.menuAlign);
31237             }
31238             this.fireEvent("click", this, e);
31239             if(this.handler){
31240                 this.el.removeClass("x-btn-over");
31241                 this.handler.call(this.scope || this, this, e);
31242             }
31243         }
31244     },
31245     // private
31246     onMouseOver : function(e){
31247         if(!this.disabled){
31248             this.el.addClass("x-btn-over");
31249             this.fireEvent('mouseover', this, e);
31250         }
31251     },
31252     // private
31253     onMouseOut : function(e){
31254         if(!e.within(this.el,  true)){
31255             this.el.removeClass("x-btn-over");
31256             this.fireEvent('mouseout', this, e);
31257         }
31258     },
31259     // private
31260     onFocus : function(e){
31261         if(!this.disabled){
31262             this.el.addClass("x-btn-focus");
31263         }
31264     },
31265     // private
31266     onBlur : function(e){
31267         this.el.removeClass("x-btn-focus");
31268     },
31269     // private
31270     onMouseDown : function(e){
31271         if(!this.disabled && e.button == 0){
31272             this.el.addClass("x-btn-click");
31273             Roo.get(document).on('mouseup', this.onMouseUp, this);
31274         }
31275     },
31276     // private
31277     onMouseUp : function(e){
31278         if(e.button == 0){
31279             this.el.removeClass("x-btn-click");
31280             Roo.get(document).un('mouseup', this.onMouseUp, this);
31281         }
31282     },
31283     // private
31284     onMenuShow : function(e){
31285         this.el.addClass("x-btn-menu-active");
31286     },
31287     // private
31288     onMenuHide : function(e){
31289         this.el.removeClass("x-btn-menu-active");
31290     }   
31291 });
31292
31293 // Private utility class used by Button
31294 Roo.ButtonToggleMgr = function(){
31295    var groups = {};
31296    
31297    function toggleGroup(btn, state){
31298        if(state){
31299            var g = groups[btn.toggleGroup];
31300            for(var i = 0, l = g.length; i < l; i++){
31301                if(g[i] != btn){
31302                    g[i].toggle(false);
31303                }
31304            }
31305        }
31306    }
31307    
31308    return {
31309        register : function(btn){
31310            if(!btn.toggleGroup){
31311                return;
31312            }
31313            var g = groups[btn.toggleGroup];
31314            if(!g){
31315                g = groups[btn.toggleGroup] = [];
31316            }
31317            g.push(btn);
31318            btn.on("toggle", toggleGroup);
31319        },
31320        
31321        unregister : function(btn){
31322            if(!btn.toggleGroup){
31323                return;
31324            }
31325            var g = groups[btn.toggleGroup];
31326            if(g){
31327                g.remove(btn);
31328                btn.un("toggle", toggleGroup);
31329            }
31330        }
31331    };
31332 }();/*
31333  * Based on:
31334  * Ext JS Library 1.1.1
31335  * Copyright(c) 2006-2007, Ext JS, LLC.
31336  *
31337  * Originally Released Under LGPL - original licence link has changed is not relivant.
31338  *
31339  * Fork - LGPL
31340  * <script type="text/javascript">
31341  */
31342  
31343 /**
31344  * @class Roo.SplitButton
31345  * @extends Roo.Button
31346  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31347  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31348  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31349  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31350  * @cfg {String} arrowTooltip The title attribute of the arrow
31351  * @constructor
31352  * Create a new menu button
31353  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31354  * @param {Object} config The config object
31355  */
31356 Roo.SplitButton = function(renderTo, config){
31357     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31358     /**
31359      * @event arrowclick
31360      * Fires when this button's arrow is clicked
31361      * @param {SplitButton} this
31362      * @param {EventObject} e The click event
31363      */
31364     this.addEvents({"arrowclick":true});
31365 };
31366
31367 Roo.extend(Roo.SplitButton, Roo.Button, {
31368     render : function(renderTo){
31369         // this is one sweet looking template!
31370         var tpl = new Roo.Template(
31371             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31372             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31373             '<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>',
31374             "</tbody></table></td><td>",
31375             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31376             '<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>',
31377             "</tbody></table></td></tr></table>"
31378         );
31379         var btn = tpl.append(renderTo, [this.text, this.type], true);
31380         var btnEl = btn.child("button");
31381         if(this.cls){
31382             btn.addClass(this.cls);
31383         }
31384         if(this.icon){
31385             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31386         }
31387         if(this.iconCls){
31388             btnEl.addClass(this.iconCls);
31389             if(!this.cls){
31390                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31391             }
31392         }
31393         this.el = btn;
31394         if(this.handleMouseEvents){
31395             btn.on("mouseover", this.onMouseOver, this);
31396             btn.on("mouseout", this.onMouseOut, this);
31397             btn.on("mousedown", this.onMouseDown, this);
31398             btn.on("mouseup", this.onMouseUp, this);
31399         }
31400         btn.on(this.clickEvent, this.onClick, this);
31401         if(this.tooltip){
31402             if(typeof this.tooltip == 'object'){
31403                 Roo.QuickTips.tips(Roo.apply({
31404                       target: btnEl.id
31405                 }, this.tooltip));
31406             } else {
31407                 btnEl.dom[this.tooltipType] = this.tooltip;
31408             }
31409         }
31410         if(this.arrowTooltip){
31411             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31412         }
31413         if(this.hidden){
31414             this.hide();
31415         }
31416         if(this.disabled){
31417             this.disable();
31418         }
31419         if(this.pressed){
31420             this.el.addClass("x-btn-pressed");
31421         }
31422         if(Roo.isIE && !Roo.isIE7){
31423             this.autoWidth.defer(1, this);
31424         }else{
31425             this.autoWidth();
31426         }
31427         if(this.menu){
31428             this.menu.on("show", this.onMenuShow, this);
31429             this.menu.on("hide", this.onMenuHide, this);
31430         }
31431         this.fireEvent('render', this);
31432     },
31433
31434     // private
31435     autoWidth : function(){
31436         if(this.el){
31437             var tbl = this.el.child("table:first");
31438             var tbl2 = this.el.child("table:last");
31439             this.el.setWidth("auto");
31440             tbl.setWidth("auto");
31441             if(Roo.isIE7 && Roo.isStrict){
31442                 var ib = this.el.child('button:first');
31443                 if(ib && ib.getWidth() > 20){
31444                     ib.clip();
31445                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31446                 }
31447             }
31448             if(this.minWidth){
31449                 if(this.hidden){
31450                     this.el.beginMeasure();
31451                 }
31452                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31453                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31454                 }
31455                 if(this.hidden){
31456                     this.el.endMeasure();
31457                 }
31458             }
31459             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31460         } 
31461     },
31462     /**
31463      * Sets this button's click handler
31464      * @param {Function} handler The function to call when the button is clicked
31465      * @param {Object} scope (optional) Scope for the function passed above
31466      */
31467     setHandler : function(handler, scope){
31468         this.handler = handler;
31469         this.scope = scope;  
31470     },
31471     
31472     /**
31473      * Sets this button's arrow click handler
31474      * @param {Function} handler The function to call when the arrow is clicked
31475      * @param {Object} scope (optional) Scope for the function passed above
31476      */
31477     setArrowHandler : function(handler, scope){
31478         this.arrowHandler = handler;
31479         this.scope = scope;  
31480     },
31481     
31482     /**
31483      * Focus the button
31484      */
31485     focus : function(){
31486         if(this.el){
31487             this.el.child("button:first").focus();
31488         }
31489     },
31490
31491     // private
31492     onClick : function(e){
31493         e.preventDefault();
31494         if(!this.disabled){
31495             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31496                 if(this.menu && !this.menu.isVisible()){
31497                     this.menu.show(this.el, this.menuAlign);
31498                 }
31499                 this.fireEvent("arrowclick", this, e);
31500                 if(this.arrowHandler){
31501                     this.arrowHandler.call(this.scope || this, this, e);
31502                 }
31503             }else{
31504                 this.fireEvent("click", this, e);
31505                 if(this.handler){
31506                     this.handler.call(this.scope || this, this, e);
31507                 }
31508             }
31509         }
31510     },
31511     // private
31512     onMouseDown : function(e){
31513         if(!this.disabled){
31514             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31515         }
31516     },
31517     // private
31518     onMouseUp : function(e){
31519         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31520     }   
31521 });
31522
31523
31524 // backwards compat
31525 Roo.MenuButton = Roo.SplitButton;/*
31526  * Based on:
31527  * Ext JS Library 1.1.1
31528  * Copyright(c) 2006-2007, Ext JS, LLC.
31529  *
31530  * Originally Released Under LGPL - original licence link has changed is not relivant.
31531  *
31532  * Fork - LGPL
31533  * <script type="text/javascript">
31534  */
31535
31536 /**
31537  * @class Roo.Toolbar
31538  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31539  * Basic Toolbar class.
31540  * @constructor
31541  * Creates a new Toolbar
31542  * @param {Object} container The config object
31543  */ 
31544 Roo.Toolbar = function(container, buttons, config)
31545 {
31546     /// old consturctor format still supported..
31547     if(container instanceof Array){ // omit the container for later rendering
31548         buttons = container;
31549         config = buttons;
31550         container = null;
31551     }
31552     if (typeof(container) == 'object' && container.xtype) {
31553         config = container;
31554         container = config.container;
31555         buttons = config.buttons || []; // not really - use items!!
31556     }
31557     var xitems = [];
31558     if (config && config.items) {
31559         xitems = config.items;
31560         delete config.items;
31561     }
31562     Roo.apply(this, config);
31563     this.buttons = buttons;
31564     
31565     if(container){
31566         this.render(container);
31567     }
31568     this.xitems = xitems;
31569     Roo.each(xitems, function(b) {
31570         this.add(b);
31571     }, this);
31572     
31573 };
31574
31575 Roo.Toolbar.prototype = {
31576     /**
31577      * @cfg {Array} items
31578      * array of button configs or elements to add (will be converted to a MixedCollection)
31579      */
31580     items: false,
31581     /**
31582      * @cfg {String/HTMLElement/Element} container
31583      * The id or element that will contain the toolbar
31584      */
31585     // private
31586     render : function(ct){
31587         this.el = Roo.get(ct);
31588         if(this.cls){
31589             this.el.addClass(this.cls);
31590         }
31591         // using a table allows for vertical alignment
31592         // 100% width is needed by Safari...
31593         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31594         this.tr = this.el.child("tr", true);
31595         var autoId = 0;
31596         this.items = new Roo.util.MixedCollection(false, function(o){
31597             return o.id || ("item" + (++autoId));
31598         });
31599         if(this.buttons){
31600             this.add.apply(this, this.buttons);
31601             delete this.buttons;
31602         }
31603     },
31604
31605     /**
31606      * Adds element(s) to the toolbar -- this function takes a variable number of 
31607      * arguments of mixed type and adds them to the toolbar.
31608      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31609      * <ul>
31610      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31611      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31612      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31613      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31614      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31615      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31616      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31617      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31618      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31619      * </ul>
31620      * @param {Mixed} arg2
31621      * @param {Mixed} etc.
31622      */
31623     add : function(){
31624         var a = arguments, l = a.length;
31625         for(var i = 0; i < l; i++){
31626             this._add(a[i]);
31627         }
31628     },
31629     // private..
31630     _add : function(el) {
31631         
31632         if (el.xtype) {
31633             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31634         }
31635         
31636         if (el.applyTo){ // some kind of form field
31637             return this.addField(el);
31638         } 
31639         if (el.render){ // some kind of Toolbar.Item
31640             return this.addItem(el);
31641         }
31642         if (typeof el == "string"){ // string
31643             if(el == "separator" || el == "-"){
31644                 return this.addSeparator();
31645             }
31646             if (el == " "){
31647                 return this.addSpacer();
31648             }
31649             if(el == "->"){
31650                 return this.addFill();
31651             }
31652             return this.addText(el);
31653             
31654         }
31655         if(el.tagName){ // element
31656             return this.addElement(el);
31657         }
31658         if(typeof el == "object"){ // must be button config?
31659             return this.addButton(el);
31660         }
31661         // and now what?!?!
31662         return false;
31663         
31664     },
31665     
31666     /**
31667      * Add an Xtype element
31668      * @param {Object} xtype Xtype Object
31669      * @return {Object} created Object
31670      */
31671     addxtype : function(e){
31672         return this.add(e);  
31673     },
31674     
31675     /**
31676      * Returns the Element for this toolbar.
31677      * @return {Roo.Element}
31678      */
31679     getEl : function(){
31680         return this.el;  
31681     },
31682     
31683     /**
31684      * Adds a separator
31685      * @return {Roo.Toolbar.Item} The separator item
31686      */
31687     addSeparator : function(){
31688         return this.addItem(new Roo.Toolbar.Separator());
31689     },
31690
31691     /**
31692      * Adds a spacer element
31693      * @return {Roo.Toolbar.Spacer} The spacer item
31694      */
31695     addSpacer : function(){
31696         return this.addItem(new Roo.Toolbar.Spacer());
31697     },
31698
31699     /**
31700      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31701      * @return {Roo.Toolbar.Fill} The fill item
31702      */
31703     addFill : function(){
31704         return this.addItem(new Roo.Toolbar.Fill());
31705     },
31706
31707     /**
31708      * Adds any standard HTML element to the toolbar
31709      * @param {String/HTMLElement/Element} el The element or id of the element to add
31710      * @return {Roo.Toolbar.Item} The element's item
31711      */
31712     addElement : function(el){
31713         return this.addItem(new Roo.Toolbar.Item(el));
31714     },
31715     /**
31716      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31717      * @type Roo.util.MixedCollection  
31718      */
31719     items : false,
31720      
31721     /**
31722      * Adds any Toolbar.Item or subclass
31723      * @param {Roo.Toolbar.Item} item
31724      * @return {Roo.Toolbar.Item} The item
31725      */
31726     addItem : function(item){
31727         var td = this.nextBlock();
31728         item.render(td);
31729         this.items.add(item);
31730         return item;
31731     },
31732     
31733     /**
31734      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31735      * @param {Object/Array} config A button config or array of configs
31736      * @return {Roo.Toolbar.Button/Array}
31737      */
31738     addButton : function(config){
31739         if(config instanceof Array){
31740             var buttons = [];
31741             for(var i = 0, len = config.length; i < len; i++) {
31742                 buttons.push(this.addButton(config[i]));
31743             }
31744             return buttons;
31745         }
31746         var b = config;
31747         if(!(config instanceof Roo.Toolbar.Button)){
31748             b = config.split ?
31749                 new Roo.Toolbar.SplitButton(config) :
31750                 new Roo.Toolbar.Button(config);
31751         }
31752         var td = this.nextBlock();
31753         b.render(td);
31754         this.items.add(b);
31755         return b;
31756     },
31757     
31758     /**
31759      * Adds text to the toolbar
31760      * @param {String} text The text to add
31761      * @return {Roo.Toolbar.Item} The element's item
31762      */
31763     addText : function(text){
31764         return this.addItem(new Roo.Toolbar.TextItem(text));
31765     },
31766     
31767     /**
31768      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31769      * @param {Number} index The index where the item is to be inserted
31770      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31771      * @return {Roo.Toolbar.Button/Item}
31772      */
31773     insertButton : function(index, item){
31774         if(item instanceof Array){
31775             var buttons = [];
31776             for(var i = 0, len = item.length; i < len; i++) {
31777                buttons.push(this.insertButton(index + i, item[i]));
31778             }
31779             return buttons;
31780         }
31781         if (!(item instanceof Roo.Toolbar.Button)){
31782            item = new Roo.Toolbar.Button(item);
31783         }
31784         var td = document.createElement("td");
31785         this.tr.insertBefore(td, this.tr.childNodes[index]);
31786         item.render(td);
31787         this.items.insert(index, item);
31788         return item;
31789     },
31790     
31791     /**
31792      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31793      * @param {Object} config
31794      * @return {Roo.Toolbar.Item} The element's item
31795      */
31796     addDom : function(config, returnEl){
31797         var td = this.nextBlock();
31798         Roo.DomHelper.overwrite(td, config);
31799         var ti = new Roo.Toolbar.Item(td.firstChild);
31800         ti.render(td);
31801         this.items.add(ti);
31802         return ti;
31803     },
31804
31805     /**
31806      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31807      * @type Roo.util.MixedCollection  
31808      */
31809     fields : false,
31810     
31811     /**
31812      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31813      * Note: the field should not have been rendered yet. For a field that has already been
31814      * rendered, use {@link #addElement}.
31815      * @param {Roo.form.Field} field
31816      * @return {Roo.ToolbarItem}
31817      */
31818      
31819       
31820     addField : function(field) {
31821         if (!this.fields) {
31822             var autoId = 0;
31823             this.fields = new Roo.util.MixedCollection(false, function(o){
31824                 return o.id || ("item" + (++autoId));
31825             });
31826
31827         }
31828         
31829         var td = this.nextBlock();
31830         field.render(td);
31831         var ti = new Roo.Toolbar.Item(td.firstChild);
31832         ti.render(td);
31833         this.items.add(ti);
31834         this.fields.add(field);
31835         return ti;
31836     },
31837     /**
31838      * Hide the toolbar
31839      * @method hide
31840      */
31841      
31842       
31843     hide : function()
31844     {
31845         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31846         this.el.child('div').hide();
31847     },
31848     /**
31849      * Show the toolbar
31850      * @method show
31851      */
31852     show : function()
31853     {
31854         this.el.child('div').show();
31855     },
31856       
31857     // private
31858     nextBlock : function(){
31859         var td = document.createElement("td");
31860         this.tr.appendChild(td);
31861         return td;
31862     },
31863
31864     // private
31865     destroy : function(){
31866         if(this.items){ // rendered?
31867             Roo.destroy.apply(Roo, this.items.items);
31868         }
31869         if(this.fields){ // rendered?
31870             Roo.destroy.apply(Roo, this.fields.items);
31871         }
31872         Roo.Element.uncache(this.el, this.tr);
31873     }
31874 };
31875
31876 /**
31877  * @class Roo.Toolbar.Item
31878  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31879  * @constructor
31880  * Creates a new Item
31881  * @param {HTMLElement} el 
31882  */
31883 Roo.Toolbar.Item = function(el){
31884     var cfg = {};
31885     if (typeof (el.xtype) != 'undefined') {
31886         cfg = el;
31887         el = cfg.el;
31888     }
31889     
31890     this.el = Roo.getDom(el);
31891     this.id = Roo.id(this.el);
31892     this.hidden = false;
31893     
31894     this.addEvents({
31895          /**
31896              * @event render
31897              * Fires when the button is rendered
31898              * @param {Button} this
31899              */
31900         'render': true
31901     });
31902     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31903 };
31904 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31905 //Roo.Toolbar.Item.prototype = {
31906     
31907     /**
31908      * Get this item's HTML Element
31909      * @return {HTMLElement}
31910      */
31911     getEl : function(){
31912        return this.el;  
31913     },
31914
31915     // private
31916     render : function(td){
31917         
31918          this.td = td;
31919         td.appendChild(this.el);
31920         
31921         this.fireEvent('render', this);
31922     },
31923     
31924     /**
31925      * Removes and destroys this item.
31926      */
31927     destroy : function(){
31928         this.td.parentNode.removeChild(this.td);
31929     },
31930     
31931     /**
31932      * Shows this item.
31933      */
31934     show: function(){
31935         this.hidden = false;
31936         this.td.style.display = "";
31937     },
31938     
31939     /**
31940      * Hides this item.
31941      */
31942     hide: function(){
31943         this.hidden = true;
31944         this.td.style.display = "none";
31945     },
31946     
31947     /**
31948      * Convenience function for boolean show/hide.
31949      * @param {Boolean} visible true to show/false to hide
31950      */
31951     setVisible: function(visible){
31952         if(visible) {
31953             this.show();
31954         }else{
31955             this.hide();
31956         }
31957     },
31958     
31959     /**
31960      * Try to focus this item.
31961      */
31962     focus : function(){
31963         Roo.fly(this.el).focus();
31964     },
31965     
31966     /**
31967      * Disables this item.
31968      */
31969     disable : function(){
31970         Roo.fly(this.td).addClass("x-item-disabled");
31971         this.disabled = true;
31972         this.el.disabled = true;
31973     },
31974     
31975     /**
31976      * Enables this item.
31977      */
31978     enable : function(){
31979         Roo.fly(this.td).removeClass("x-item-disabled");
31980         this.disabled = false;
31981         this.el.disabled = false;
31982     }
31983 });
31984
31985
31986 /**
31987  * @class Roo.Toolbar.Separator
31988  * @extends Roo.Toolbar.Item
31989  * A simple toolbar separator class
31990  * @constructor
31991  * Creates a new Separator
31992  */
31993 Roo.Toolbar.Separator = function(cfg){
31994     
31995     var s = document.createElement("span");
31996     s.className = "ytb-sep";
31997     if (cfg) {
31998         cfg.el = s;
31999     }
32000     
32001     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
32002 };
32003 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
32004     enable:Roo.emptyFn,
32005     disable:Roo.emptyFn,
32006     focus:Roo.emptyFn
32007 });
32008
32009 /**
32010  * @class Roo.Toolbar.Spacer
32011  * @extends Roo.Toolbar.Item
32012  * A simple element that adds extra horizontal space to a toolbar.
32013  * @constructor
32014  * Creates a new Spacer
32015  */
32016 Roo.Toolbar.Spacer = function(cfg){
32017     var s = document.createElement("div");
32018     s.className = "ytb-spacer";
32019     if (cfg) {
32020         cfg.el = s;
32021     }
32022     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
32023 };
32024 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
32025     enable:Roo.emptyFn,
32026     disable:Roo.emptyFn,
32027     focus:Roo.emptyFn
32028 });
32029
32030 /**
32031  * @class Roo.Toolbar.Fill
32032  * @extends Roo.Toolbar.Spacer
32033  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32034  * @constructor
32035  * Creates a new Spacer
32036  */
32037 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32038     // private
32039     render : function(td){
32040         td.style.width = '100%';
32041         Roo.Toolbar.Fill.superclass.render.call(this, td);
32042     }
32043 });
32044
32045 /**
32046  * @class Roo.Toolbar.TextItem
32047  * @extends Roo.Toolbar.Item
32048  * A simple class that renders text directly into a toolbar.
32049  * @constructor
32050  * Creates a new TextItem
32051  * @cfg {string} text 
32052  */
32053 Roo.Toolbar.TextItem = function(cfg){
32054     var  text = cfg || "";
32055     if (typeof(cfg) == 'object') {
32056         text = cfg.text || "";
32057     }  else {
32058         cfg = null;
32059     }
32060     var s = document.createElement("span");
32061     s.className = "ytb-text";
32062     s.innerHTML = text;
32063     if (cfg) {
32064         cfg.el  = s;
32065     }
32066     
32067     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
32068 };
32069 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32070     
32071      
32072     enable:Roo.emptyFn,
32073     disable:Roo.emptyFn,
32074     focus:Roo.emptyFn,
32075      /**
32076      * Shows this button
32077      */
32078     show: function(){
32079         this.hidden = false;
32080         this.el.style.display = "";
32081     },
32082     
32083     /**
32084      * Hides this button
32085      */
32086     hide: function(){
32087         this.hidden = true;
32088         this.el.style.display = "none";
32089     }
32090     
32091 });
32092
32093 /**
32094  * @class Roo.Toolbar.Button
32095  * @extends Roo.Button
32096  * A button that renders into a toolbar.
32097  * @constructor
32098  * Creates a new Button
32099  * @param {Object} config A standard {@link Roo.Button} config object
32100  */
32101 Roo.Toolbar.Button = function(config){
32102     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32103 };
32104 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32105 {
32106     
32107     
32108     render : function(td){
32109         this.td = td;
32110         Roo.Toolbar.Button.superclass.render.call(this, td);
32111     },
32112     
32113     /**
32114      * Removes and destroys this button
32115      */
32116     destroy : function(){
32117         Roo.Toolbar.Button.superclass.destroy.call(this);
32118         this.td.parentNode.removeChild(this.td);
32119     },
32120     
32121     /**
32122      * Shows this button
32123      */
32124     show: function(){
32125         this.hidden = false;
32126         this.td.style.display = "";
32127     },
32128     
32129     /**
32130      * Hides this button
32131      */
32132     hide: function(){
32133         this.hidden = true;
32134         this.td.style.display = "none";
32135     },
32136
32137     /**
32138      * Disables this item
32139      */
32140     disable : function(){
32141         Roo.fly(this.td).addClass("x-item-disabled");
32142         this.disabled = true;
32143     },
32144
32145     /**
32146      * Enables this item
32147      */
32148     enable : function(){
32149         Roo.fly(this.td).removeClass("x-item-disabled");
32150         this.disabled = false;
32151     }
32152 });
32153 // backwards compat
32154 Roo.ToolbarButton = Roo.Toolbar.Button;
32155
32156 /**
32157  * @class Roo.Toolbar.SplitButton
32158  * @extends Roo.SplitButton
32159  * A menu button that renders into a toolbar.
32160  * @constructor
32161  * Creates a new SplitButton
32162  * @param {Object} config A standard {@link Roo.SplitButton} config object
32163  */
32164 Roo.Toolbar.SplitButton = function(config){
32165     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32166 };
32167 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32168     render : function(td){
32169         this.td = td;
32170         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32171     },
32172     
32173     /**
32174      * Removes and destroys this button
32175      */
32176     destroy : function(){
32177         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32178         this.td.parentNode.removeChild(this.td);
32179     },
32180     
32181     /**
32182      * Shows this button
32183      */
32184     show: function(){
32185         this.hidden = false;
32186         this.td.style.display = "";
32187     },
32188     
32189     /**
32190      * Hides this button
32191      */
32192     hide: function(){
32193         this.hidden = true;
32194         this.td.style.display = "none";
32195     }
32196 });
32197
32198 // backwards compat
32199 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32200  * Based on:
32201  * Ext JS Library 1.1.1
32202  * Copyright(c) 2006-2007, Ext JS, LLC.
32203  *
32204  * Originally Released Under LGPL - original licence link has changed is not relivant.
32205  *
32206  * Fork - LGPL
32207  * <script type="text/javascript">
32208  */
32209  
32210 /**
32211  * @class Roo.PagingToolbar
32212  * @extends Roo.Toolbar
32213  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32214  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32215  * @constructor
32216  * Create a new PagingToolbar
32217  * @param {Object} config The config object
32218  */
32219 Roo.PagingToolbar = function(el, ds, config)
32220 {
32221     // old args format still supported... - xtype is prefered..
32222     if (typeof(el) == 'object' && el.xtype) {
32223         // created from xtype...
32224         config = el;
32225         ds = el.dataSource;
32226         el = config.container;
32227     }
32228     var items = [];
32229     if (config.items) {
32230         items = config.items;
32231         config.items = [];
32232     }
32233     
32234     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32235     this.ds = ds;
32236     this.cursor = 0;
32237     this.renderButtons(this.el);
32238     this.bind(ds);
32239     
32240     // supprot items array.
32241    
32242     Roo.each(items, function(e) {
32243         this.add(Roo.factory(e));
32244     },this);
32245     
32246 };
32247
32248 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32249    
32250     /**
32251      * @cfg {String/HTMLElement/Element} container
32252      * container The id or element that will contain the toolbar
32253      */
32254     /**
32255      * @cfg {Boolean} displayInfo
32256      * True to display the displayMsg (defaults to false)
32257      */
32258     
32259     
32260     /**
32261      * @cfg {Number} pageSize
32262      * The number of records to display per page (defaults to 20)
32263      */
32264     pageSize: 20,
32265     /**
32266      * @cfg {String} displayMsg
32267      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32268      */
32269     displayMsg : 'Displaying {0} - {1} of {2}',
32270     /**
32271      * @cfg {String} emptyMsg
32272      * The message to display when no records are found (defaults to "No data to display")
32273      */
32274     emptyMsg : 'No data to display',
32275     /**
32276      * Customizable piece of the default paging text (defaults to "Page")
32277      * @type String
32278      */
32279     beforePageText : "Page",
32280     /**
32281      * Customizable piece of the default paging text (defaults to "of %0")
32282      * @type String
32283      */
32284     afterPageText : "of {0}",
32285     /**
32286      * Customizable piece of the default paging text (defaults to "First Page")
32287      * @type String
32288      */
32289     firstText : "First Page",
32290     /**
32291      * Customizable piece of the default paging text (defaults to "Previous Page")
32292      * @type String
32293      */
32294     prevText : "Previous Page",
32295     /**
32296      * Customizable piece of the default paging text (defaults to "Next Page")
32297      * @type String
32298      */
32299     nextText : "Next Page",
32300     /**
32301      * Customizable piece of the default paging text (defaults to "Last Page")
32302      * @type String
32303      */
32304     lastText : "Last Page",
32305     /**
32306      * Customizable piece of the default paging text (defaults to "Refresh")
32307      * @type String
32308      */
32309     refreshText : "Refresh",
32310
32311     // private
32312     renderButtons : function(el){
32313         Roo.PagingToolbar.superclass.render.call(this, el);
32314         this.first = this.addButton({
32315             tooltip: this.firstText,
32316             cls: "x-btn-icon x-grid-page-first",
32317             disabled: true,
32318             handler: this.onClick.createDelegate(this, ["first"])
32319         });
32320         this.prev = this.addButton({
32321             tooltip: this.prevText,
32322             cls: "x-btn-icon x-grid-page-prev",
32323             disabled: true,
32324             handler: this.onClick.createDelegate(this, ["prev"])
32325         });
32326         //this.addSeparator();
32327         this.add(this.beforePageText);
32328         this.field = Roo.get(this.addDom({
32329            tag: "input",
32330            type: "text",
32331            size: "3",
32332            value: "1",
32333            cls: "x-grid-page-number"
32334         }).el);
32335         this.field.on("keydown", this.onPagingKeydown, this);
32336         this.field.on("focus", function(){this.dom.select();});
32337         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32338         this.field.setHeight(18);
32339         //this.addSeparator();
32340         this.next = this.addButton({
32341             tooltip: this.nextText,
32342             cls: "x-btn-icon x-grid-page-next",
32343             disabled: true,
32344             handler: this.onClick.createDelegate(this, ["next"])
32345         });
32346         this.last = this.addButton({
32347             tooltip: this.lastText,
32348             cls: "x-btn-icon x-grid-page-last",
32349             disabled: true,
32350             handler: this.onClick.createDelegate(this, ["last"])
32351         });
32352         //this.addSeparator();
32353         this.loading = this.addButton({
32354             tooltip: this.refreshText,
32355             cls: "x-btn-icon x-grid-loading",
32356             handler: this.onClick.createDelegate(this, ["refresh"])
32357         });
32358
32359         if(this.displayInfo){
32360             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32361         }
32362     },
32363
32364     // private
32365     updateInfo : function(){
32366         if(this.displayEl){
32367             var count = this.ds.getCount();
32368             var msg = count == 0 ?
32369                 this.emptyMsg :
32370                 String.format(
32371                     this.displayMsg,
32372                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32373                 );
32374             this.displayEl.update(msg);
32375         }
32376     },
32377
32378     // private
32379     onLoad : function(ds, r, o){
32380        this.cursor = o.params ? o.params.start : 0;
32381        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32382
32383        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32384        this.field.dom.value = ap;
32385        this.first.setDisabled(ap == 1);
32386        this.prev.setDisabled(ap == 1);
32387        this.next.setDisabled(ap == ps);
32388        this.last.setDisabled(ap == ps);
32389        this.loading.enable();
32390        this.updateInfo();
32391     },
32392
32393     // private
32394     getPageData : function(){
32395         var total = this.ds.getTotalCount();
32396         return {
32397             total : total,
32398             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32399             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32400         };
32401     },
32402
32403     // private
32404     onLoadError : function(){
32405         this.loading.enable();
32406     },
32407
32408     // private
32409     onPagingKeydown : function(e){
32410         var k = e.getKey();
32411         var d = this.getPageData();
32412         if(k == e.RETURN){
32413             var v = this.field.dom.value, pageNum;
32414             if(!v || isNaN(pageNum = parseInt(v, 10))){
32415                 this.field.dom.value = d.activePage;
32416                 return;
32417             }
32418             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32419             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32420             e.stopEvent();
32421         }
32422         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))
32423         {
32424           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32425           this.field.dom.value = pageNum;
32426           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32427           e.stopEvent();
32428         }
32429         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32430         {
32431           var v = this.field.dom.value, pageNum; 
32432           var increment = (e.shiftKey) ? 10 : 1;
32433           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32434             increment *= -1;
32435           }
32436           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32437             this.field.dom.value = d.activePage;
32438             return;
32439           }
32440           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32441           {
32442             this.field.dom.value = parseInt(v, 10) + increment;
32443             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32444             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32445           }
32446           e.stopEvent();
32447         }
32448     },
32449
32450     // private
32451     beforeLoad : function(){
32452         if(this.loading){
32453             this.loading.disable();
32454         }
32455     },
32456     /**
32457      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32458      * @param {String} which (first|prev|next|last|refresh)  which button to press.
32459      *
32460      */
32461     // private
32462     onClick : function(which){
32463         var ds = this.ds;
32464         switch(which){
32465             case "first":
32466                 ds.load({params:{start: 0, limit: this.pageSize}});
32467             break;
32468             case "prev":
32469                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32470             break;
32471             case "next":
32472                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32473             break;
32474             case "last":
32475                 var total = ds.getTotalCount();
32476                 var extra = total % this.pageSize;
32477                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32478                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32479             break;
32480             case "refresh":
32481                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32482             break;
32483         }
32484     },
32485
32486     /**
32487      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32488      * @param {Roo.data.Store} store The data store to unbind
32489      */
32490     unbind : function(ds){
32491         ds.un("beforeload", this.beforeLoad, this);
32492         ds.un("load", this.onLoad, this);
32493         ds.un("loadexception", this.onLoadError, this);
32494         ds.un("remove", this.updateInfo, this);
32495         ds.un("add", this.updateInfo, this);
32496         this.ds = undefined;
32497     },
32498
32499     /**
32500      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32501      * @param {Roo.data.Store} store The data store to bind
32502      */
32503     bind : function(ds){
32504         ds.on("beforeload", this.beforeLoad, this);
32505         ds.on("load", this.onLoad, this);
32506         ds.on("loadexception", this.onLoadError, this);
32507         ds.on("remove", this.updateInfo, this);
32508         ds.on("add", this.updateInfo, this);
32509         this.ds = ds;
32510     }
32511 });/*
32512  * Based on:
32513  * Ext JS Library 1.1.1
32514  * Copyright(c) 2006-2007, Ext JS, LLC.
32515  *
32516  * Originally Released Under LGPL - original licence link has changed is not relivant.
32517  *
32518  * Fork - LGPL
32519  * <script type="text/javascript">
32520  */
32521
32522 /**
32523  * @class Roo.Resizable
32524  * @extends Roo.util.Observable
32525  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32526  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32527  * 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
32528  * the element will be wrapped for you automatically.</p>
32529  * <p>Here is the list of valid resize handles:</p>
32530  * <pre>
32531 Value   Description
32532 ------  -------------------
32533  'n'     north
32534  's'     south
32535  'e'     east
32536  'w'     west
32537  'nw'    northwest
32538  'sw'    southwest
32539  'se'    southeast
32540  'ne'    northeast
32541  'hd'    horizontal drag
32542  'all'   all
32543 </pre>
32544  * <p>Here's an example showing the creation of a typical Resizable:</p>
32545  * <pre><code>
32546 var resizer = new Roo.Resizable("element-id", {
32547     handles: 'all',
32548     minWidth: 200,
32549     minHeight: 100,
32550     maxWidth: 500,
32551     maxHeight: 400,
32552     pinned: true
32553 });
32554 resizer.on("resize", myHandler);
32555 </code></pre>
32556  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32557  * resizer.east.setDisplayed(false);</p>
32558  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32559  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32560  * resize operation's new size (defaults to [0, 0])
32561  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32562  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32563  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32564  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32565  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32566  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32567  * @cfg {Number} width The width of the element in pixels (defaults to null)
32568  * @cfg {Number} height The height of the element in pixels (defaults to null)
32569  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32570  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32571  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32572  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32573  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32574  * in favor of the handles config option (defaults to false)
32575  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32576  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32577  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32578  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32579  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32580  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32581  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32582  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32583  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32584  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32585  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32586  * @constructor
32587  * Create a new resizable component
32588  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32589  * @param {Object} config configuration options
32590   */
32591 Roo.Resizable = function(el, config)
32592 {
32593     this.el = Roo.get(el);
32594
32595     if(config && config.wrap){
32596         config.resizeChild = this.el;
32597         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32598         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32599         this.el.setStyle("overflow", "hidden");
32600         this.el.setPositioning(config.resizeChild.getPositioning());
32601         config.resizeChild.clearPositioning();
32602         if(!config.width || !config.height){
32603             var csize = config.resizeChild.getSize();
32604             this.el.setSize(csize.width, csize.height);
32605         }
32606         if(config.pinned && !config.adjustments){
32607             config.adjustments = "auto";
32608         }
32609     }
32610
32611     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32612     this.proxy.unselectable();
32613     this.proxy.enableDisplayMode('block');
32614
32615     Roo.apply(this, config);
32616
32617     if(this.pinned){
32618         this.disableTrackOver = true;
32619         this.el.addClass("x-resizable-pinned");
32620     }
32621     // if the element isn't positioned, make it relative
32622     var position = this.el.getStyle("position");
32623     if(position != "absolute" && position != "fixed"){
32624         this.el.setStyle("position", "relative");
32625     }
32626     if(!this.handles){ // no handles passed, must be legacy style
32627         this.handles = 's,e,se';
32628         if(this.multiDirectional){
32629             this.handles += ',n,w';
32630         }
32631     }
32632     if(this.handles == "all"){
32633         this.handles = "n s e w ne nw se sw";
32634     }
32635     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32636     var ps = Roo.Resizable.positions;
32637     for(var i = 0, len = hs.length; i < len; i++){
32638         if(hs[i] && ps[hs[i]]){
32639             var pos = ps[hs[i]];
32640             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32641         }
32642     }
32643     // legacy
32644     this.corner = this.southeast;
32645     
32646     // updateBox = the box can move..
32647     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32648         this.updateBox = true;
32649     }
32650
32651     this.activeHandle = null;
32652
32653     if(this.resizeChild){
32654         if(typeof this.resizeChild == "boolean"){
32655             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32656         }else{
32657             this.resizeChild = Roo.get(this.resizeChild, true);
32658         }
32659     }
32660     
32661     if(this.adjustments == "auto"){
32662         var rc = this.resizeChild;
32663         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32664         if(rc && (hw || hn)){
32665             rc.position("relative");
32666             rc.setLeft(hw ? hw.el.getWidth() : 0);
32667             rc.setTop(hn ? hn.el.getHeight() : 0);
32668         }
32669         this.adjustments = [
32670             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32671             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32672         ];
32673     }
32674
32675     if(this.draggable){
32676         this.dd = this.dynamic ?
32677             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32678         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32679     }
32680
32681     // public events
32682     this.addEvents({
32683         /**
32684          * @event beforeresize
32685          * Fired before resize is allowed. Set enabled to false to cancel resize.
32686          * @param {Roo.Resizable} this
32687          * @param {Roo.EventObject} e The mousedown event
32688          */
32689         "beforeresize" : true,
32690         /**
32691          * @event resizing
32692          * Fired a resizing.
32693          * @param {Roo.Resizable} this
32694          * @param {Number} x The new x position
32695          * @param {Number} y The new y position
32696          * @param {Number} w The new w width
32697          * @param {Number} h The new h hight
32698          * @param {Roo.EventObject} e The mouseup event
32699          */
32700         "resizing" : true,
32701         /**
32702          * @event resize
32703          * Fired after a resize.
32704          * @param {Roo.Resizable} this
32705          * @param {Number} width The new width
32706          * @param {Number} height The new height
32707          * @param {Roo.EventObject} e The mouseup event
32708          */
32709         "resize" : true
32710     });
32711
32712     if(this.width !== null && this.height !== null){
32713         this.resizeTo(this.width, this.height);
32714     }else{
32715         this.updateChildSize();
32716     }
32717     if(Roo.isIE){
32718         this.el.dom.style.zoom = 1;
32719     }
32720     Roo.Resizable.superclass.constructor.call(this);
32721 };
32722
32723 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32724         resizeChild : false,
32725         adjustments : [0, 0],
32726         minWidth : 5,
32727         minHeight : 5,
32728         maxWidth : 10000,
32729         maxHeight : 10000,
32730         enabled : true,
32731         animate : false,
32732         duration : .35,
32733         dynamic : false,
32734         handles : false,
32735         multiDirectional : false,
32736         disableTrackOver : false,
32737         easing : 'easeOutStrong',
32738         widthIncrement : 0,
32739         heightIncrement : 0,
32740         pinned : false,
32741         width : null,
32742         height : null,
32743         preserveRatio : false,
32744         transparent: false,
32745         minX: 0,
32746         minY: 0,
32747         draggable: false,
32748
32749         /**
32750          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32751          */
32752         constrainTo: undefined,
32753         /**
32754          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32755          */
32756         resizeRegion: undefined,
32757
32758
32759     /**
32760      * Perform a manual resize
32761      * @param {Number} width
32762      * @param {Number} height
32763      */
32764     resizeTo : function(width, height){
32765         this.el.setSize(width, height);
32766         this.updateChildSize();
32767         this.fireEvent("resize", this, width, height, null);
32768     },
32769
32770     // private
32771     startSizing : function(e, handle){
32772         this.fireEvent("beforeresize", this, e);
32773         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32774
32775             if(!this.overlay){
32776                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32777                 this.overlay.unselectable();
32778                 this.overlay.enableDisplayMode("block");
32779                 this.overlay.on("mousemove", this.onMouseMove, this);
32780                 this.overlay.on("mouseup", this.onMouseUp, this);
32781             }
32782             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32783
32784             this.resizing = true;
32785             this.startBox = this.el.getBox();
32786             this.startPoint = e.getXY();
32787             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32788                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32789
32790             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32791             this.overlay.show();
32792
32793             if(this.constrainTo) {
32794                 var ct = Roo.get(this.constrainTo);
32795                 this.resizeRegion = ct.getRegion().adjust(
32796                     ct.getFrameWidth('t'),
32797                     ct.getFrameWidth('l'),
32798                     -ct.getFrameWidth('b'),
32799                     -ct.getFrameWidth('r')
32800                 );
32801             }
32802
32803             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32804             this.proxy.show();
32805             this.proxy.setBox(this.startBox);
32806             if(!this.dynamic){
32807                 this.proxy.setStyle('visibility', 'visible');
32808             }
32809         }
32810     },
32811
32812     // private
32813     onMouseDown : function(handle, e){
32814         if(this.enabled){
32815             e.stopEvent();
32816             this.activeHandle = handle;
32817             this.startSizing(e, handle);
32818         }
32819     },
32820
32821     // private
32822     onMouseUp : function(e){
32823         var size = this.resizeElement();
32824         this.resizing = false;
32825         this.handleOut();
32826         this.overlay.hide();
32827         this.proxy.hide();
32828         this.fireEvent("resize", this, size.width, size.height, e);
32829     },
32830
32831     // private
32832     updateChildSize : function(){
32833         
32834         if(this.resizeChild){
32835             var el = this.el;
32836             var child = this.resizeChild;
32837             var adj = this.adjustments;
32838             if(el.dom.offsetWidth){
32839                 var b = el.getSize(true);
32840                 child.setSize(b.width+adj[0], b.height+adj[1]);
32841             }
32842             // Second call here for IE
32843             // The first call enables instant resizing and
32844             // the second call corrects scroll bars if they
32845             // exist
32846             if(Roo.isIE){
32847                 setTimeout(function(){
32848                     if(el.dom.offsetWidth){
32849                         var b = el.getSize(true);
32850                         child.setSize(b.width+adj[0], b.height+adj[1]);
32851                     }
32852                 }, 10);
32853             }
32854         }
32855     },
32856
32857     // private
32858     snap : function(value, inc, min){
32859         if(!inc || !value) {
32860             return value;
32861         }
32862         var newValue = value;
32863         var m = value % inc;
32864         if(m > 0){
32865             if(m > (inc/2)){
32866                 newValue = value + (inc-m);
32867             }else{
32868                 newValue = value - m;
32869             }
32870         }
32871         return Math.max(min, newValue);
32872     },
32873
32874     // private
32875     resizeElement : function(){
32876         var box = this.proxy.getBox();
32877         if(this.updateBox){
32878             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32879         }else{
32880             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32881         }
32882         this.updateChildSize();
32883         if(!this.dynamic){
32884             this.proxy.hide();
32885         }
32886         return box;
32887     },
32888
32889     // private
32890     constrain : function(v, diff, m, mx){
32891         if(v - diff < m){
32892             diff = v - m;
32893         }else if(v - diff > mx){
32894             diff = mx - v;
32895         }
32896         return diff;
32897     },
32898
32899     // private
32900     onMouseMove : function(e){
32901         
32902         if(this.enabled){
32903             try{// try catch so if something goes wrong the user doesn't get hung
32904
32905             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32906                 return;
32907             }
32908
32909             //var curXY = this.startPoint;
32910             var curSize = this.curSize || this.startBox;
32911             var x = this.startBox.x, y = this.startBox.y;
32912             var ox = x, oy = y;
32913             var w = curSize.width, h = curSize.height;
32914             var ow = w, oh = h;
32915             var mw = this.minWidth, mh = this.minHeight;
32916             var mxw = this.maxWidth, mxh = this.maxHeight;
32917             var wi = this.widthIncrement;
32918             var hi = this.heightIncrement;
32919
32920             var eventXY = e.getXY();
32921             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32922             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32923
32924             var pos = this.activeHandle.position;
32925
32926             switch(pos){
32927                 case "east":
32928                     w += diffX;
32929                     w = Math.min(Math.max(mw, w), mxw);
32930                     break;
32931              
32932                 case "south":
32933                     h += diffY;
32934                     h = Math.min(Math.max(mh, h), mxh);
32935                     break;
32936                 case "southeast":
32937                     w += diffX;
32938                     h += diffY;
32939                     w = Math.min(Math.max(mw, w), mxw);
32940                     h = Math.min(Math.max(mh, h), mxh);
32941                     break;
32942                 case "north":
32943                     diffY = this.constrain(h, diffY, mh, mxh);
32944                     y += diffY;
32945                     h -= diffY;
32946                     break;
32947                 case "hdrag":
32948                     
32949                     if (wi) {
32950                         var adiffX = Math.abs(diffX);
32951                         var sub = (adiffX % wi); // how much 
32952                         if (sub > (wi/2)) { // far enough to snap
32953                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32954                         } else {
32955                             // remove difference.. 
32956                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32957                         }
32958                     }
32959                     x += diffX;
32960                     x = Math.max(this.minX, x);
32961                     break;
32962                 case "west":
32963                     diffX = this.constrain(w, diffX, mw, mxw);
32964                     x += diffX;
32965                     w -= diffX;
32966                     break;
32967                 case "northeast":
32968                     w += diffX;
32969                     w = Math.min(Math.max(mw, w), mxw);
32970                     diffY = this.constrain(h, diffY, mh, mxh);
32971                     y += diffY;
32972                     h -= diffY;
32973                     break;
32974                 case "northwest":
32975                     diffX = this.constrain(w, diffX, mw, mxw);
32976                     diffY = this.constrain(h, diffY, mh, mxh);
32977                     y += diffY;
32978                     h -= diffY;
32979                     x += diffX;
32980                     w -= diffX;
32981                     break;
32982                case "southwest":
32983                     diffX = this.constrain(w, diffX, mw, mxw);
32984                     h += diffY;
32985                     h = Math.min(Math.max(mh, h), mxh);
32986                     x += diffX;
32987                     w -= diffX;
32988                     break;
32989             }
32990
32991             var sw = this.snap(w, wi, mw);
32992             var sh = this.snap(h, hi, mh);
32993             if(sw != w || sh != h){
32994                 switch(pos){
32995                     case "northeast":
32996                         y -= sh - h;
32997                     break;
32998                     case "north":
32999                         y -= sh - h;
33000                         break;
33001                     case "southwest":
33002                         x -= sw - w;
33003                     break;
33004                     case "west":
33005                         x -= sw - w;
33006                         break;
33007                     case "northwest":
33008                         x -= sw - w;
33009                         y -= sh - h;
33010                     break;
33011                 }
33012                 w = sw;
33013                 h = sh;
33014             }
33015
33016             if(this.preserveRatio){
33017                 switch(pos){
33018                     case "southeast":
33019                     case "east":
33020                         h = oh * (w/ow);
33021                         h = Math.min(Math.max(mh, h), mxh);
33022                         w = ow * (h/oh);
33023                        break;
33024                     case "south":
33025                         w = ow * (h/oh);
33026                         w = Math.min(Math.max(mw, w), mxw);
33027                         h = oh * (w/ow);
33028                         break;
33029                     case "northeast":
33030                         w = ow * (h/oh);
33031                         w = Math.min(Math.max(mw, w), mxw);
33032                         h = oh * (w/ow);
33033                     break;
33034                     case "north":
33035                         var tw = w;
33036                         w = ow * (h/oh);
33037                         w = Math.min(Math.max(mw, w), mxw);
33038                         h = oh * (w/ow);
33039                         x += (tw - w) / 2;
33040                         break;
33041                     case "southwest":
33042                         h = oh * (w/ow);
33043                         h = Math.min(Math.max(mh, h), mxh);
33044                         var tw = w;
33045                         w = ow * (h/oh);
33046                         x += tw - w;
33047                         break;
33048                     case "west":
33049                         var th = h;
33050                         h = oh * (w/ow);
33051                         h = Math.min(Math.max(mh, h), mxh);
33052                         y += (th - h) / 2;
33053                         var tw = w;
33054                         w = ow * (h/oh);
33055                         x += tw - w;
33056                        break;
33057                     case "northwest":
33058                         var tw = w;
33059                         var th = h;
33060                         h = oh * (w/ow);
33061                         h = Math.min(Math.max(mh, h), mxh);
33062                         w = ow * (h/oh);
33063                         y += th - h;
33064                         x += tw - w;
33065                        break;
33066
33067                 }
33068             }
33069             if (pos == 'hdrag') {
33070                 w = ow;
33071             }
33072             this.proxy.setBounds(x, y, w, h);
33073             if(this.dynamic){
33074                 this.resizeElement();
33075             }
33076             }catch(e){}
33077         }
33078         this.fireEvent("resizing", this, x, y, w, h, e);
33079     },
33080
33081     // private
33082     handleOver : function(){
33083         if(this.enabled){
33084             this.el.addClass("x-resizable-over");
33085         }
33086     },
33087
33088     // private
33089     handleOut : function(){
33090         if(!this.resizing){
33091             this.el.removeClass("x-resizable-over");
33092         }
33093     },
33094
33095     /**
33096      * Returns the element this component is bound to.
33097      * @return {Roo.Element}
33098      */
33099     getEl : function(){
33100         return this.el;
33101     },
33102
33103     /**
33104      * Returns the resizeChild element (or null).
33105      * @return {Roo.Element}
33106      */
33107     getResizeChild : function(){
33108         return this.resizeChild;
33109     },
33110     groupHandler : function()
33111     {
33112         
33113     },
33114     /**
33115      * Destroys this resizable. If the element was wrapped and
33116      * removeEl is not true then the element remains.
33117      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33118      */
33119     destroy : function(removeEl){
33120         this.proxy.remove();
33121         if(this.overlay){
33122             this.overlay.removeAllListeners();
33123             this.overlay.remove();
33124         }
33125         var ps = Roo.Resizable.positions;
33126         for(var k in ps){
33127             if(typeof ps[k] != "function" && this[ps[k]]){
33128                 var h = this[ps[k]];
33129                 h.el.removeAllListeners();
33130                 h.el.remove();
33131             }
33132         }
33133         if(removeEl){
33134             this.el.update("");
33135             this.el.remove();
33136         }
33137     }
33138 });
33139
33140 // private
33141 // hash to map config positions to true positions
33142 Roo.Resizable.positions = {
33143     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
33144     hd: "hdrag"
33145 };
33146
33147 // private
33148 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33149     if(!this.tpl){
33150         // only initialize the template if resizable is used
33151         var tpl = Roo.DomHelper.createTemplate(
33152             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33153         );
33154         tpl.compile();
33155         Roo.Resizable.Handle.prototype.tpl = tpl;
33156     }
33157     this.position = pos;
33158     this.rz = rz;
33159     // show north drag fro topdra
33160     var handlepos = pos == 'hdrag' ? 'north' : pos;
33161     
33162     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33163     if (pos == 'hdrag') {
33164         this.el.setStyle('cursor', 'pointer');
33165     }
33166     this.el.unselectable();
33167     if(transparent){
33168         this.el.setOpacity(0);
33169     }
33170     this.el.on("mousedown", this.onMouseDown, this);
33171     if(!disableTrackOver){
33172         this.el.on("mouseover", this.onMouseOver, this);
33173         this.el.on("mouseout", this.onMouseOut, this);
33174     }
33175 };
33176
33177 // private
33178 Roo.Resizable.Handle.prototype = {
33179     afterResize : function(rz){
33180         Roo.log('after?');
33181         // do nothing
33182     },
33183     // private
33184     onMouseDown : function(e){
33185         this.rz.onMouseDown(this, e);
33186     },
33187     // private
33188     onMouseOver : function(e){
33189         this.rz.handleOver(this, e);
33190     },
33191     // private
33192     onMouseOut : function(e){
33193         this.rz.handleOut(this, e);
33194     }
33195 };/*
33196  * Based on:
33197  * Ext JS Library 1.1.1
33198  * Copyright(c) 2006-2007, Ext JS, LLC.
33199  *
33200  * Originally Released Under LGPL - original licence link has changed is not relivant.
33201  *
33202  * Fork - LGPL
33203  * <script type="text/javascript">
33204  */
33205
33206 /**
33207  * @class Roo.Editor
33208  * @extends Roo.Component
33209  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33210  * @constructor
33211  * Create a new Editor
33212  * @param {Roo.form.Field} field The Field object (or descendant)
33213  * @param {Object} config The config object
33214  */
33215 Roo.Editor = function(field, config){
33216     Roo.Editor.superclass.constructor.call(this, config);
33217     this.field = field;
33218     this.addEvents({
33219         /**
33220              * @event beforestartedit
33221              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33222              * false from the handler of this event.
33223              * @param {Editor} this
33224              * @param {Roo.Element} boundEl The underlying element bound to this editor
33225              * @param {Mixed} value The field value being set
33226              */
33227         "beforestartedit" : true,
33228         /**
33229              * @event startedit
33230              * Fires when this editor is displayed
33231              * @param {Roo.Element} boundEl The underlying element bound to this editor
33232              * @param {Mixed} value The starting field value
33233              */
33234         "startedit" : true,
33235         /**
33236              * @event beforecomplete
33237              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33238              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33239              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33240              * event will not fire since no edit actually occurred.
33241              * @param {Editor} this
33242              * @param {Mixed} value The current field value
33243              * @param {Mixed} startValue The original field value
33244              */
33245         "beforecomplete" : true,
33246         /**
33247              * @event complete
33248              * Fires after editing is complete and any changed value has been written to the underlying field.
33249              * @param {Editor} this
33250              * @param {Mixed} value The current field value
33251              * @param {Mixed} startValue The original field value
33252              */
33253         "complete" : true,
33254         /**
33255          * @event specialkey
33256          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33257          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33258          * @param {Roo.form.Field} this
33259          * @param {Roo.EventObject} e The event object
33260          */
33261         "specialkey" : true
33262     });
33263 };
33264
33265 Roo.extend(Roo.Editor, Roo.Component, {
33266     /**
33267      * @cfg {Boolean/String} autosize
33268      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33269      * or "height" to adopt the height only (defaults to false)
33270      */
33271     /**
33272      * @cfg {Boolean} revertInvalid
33273      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33274      * validation fails (defaults to true)
33275      */
33276     /**
33277      * @cfg {Boolean} ignoreNoChange
33278      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33279      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33280      * will never be ignored.
33281      */
33282     /**
33283      * @cfg {Boolean} hideEl
33284      * False to keep the bound element visible while the editor is displayed (defaults to true)
33285      */
33286     /**
33287      * @cfg {Mixed} value
33288      * The data value of the underlying field (defaults to "")
33289      */
33290     value : "",
33291     /**
33292      * @cfg {String} alignment
33293      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33294      */
33295     alignment: "c-c?",
33296     /**
33297      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33298      * for bottom-right shadow (defaults to "frame")
33299      */
33300     shadow : "frame",
33301     /**
33302      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33303      */
33304     constrain : false,
33305     /**
33306      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33307      */
33308     completeOnEnter : false,
33309     /**
33310      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33311      */
33312     cancelOnEsc : false,
33313     /**
33314      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33315      */
33316     updateEl : false,
33317
33318     // private
33319     onRender : function(ct, position){
33320         this.el = new Roo.Layer({
33321             shadow: this.shadow,
33322             cls: "x-editor",
33323             parentEl : ct,
33324             shim : this.shim,
33325             shadowOffset:4,
33326             id: this.id,
33327             constrain: this.constrain
33328         });
33329         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33330         if(this.field.msgTarget != 'title'){
33331             this.field.msgTarget = 'qtip';
33332         }
33333         this.field.render(this.el);
33334         if(Roo.isGecko){
33335             this.field.el.dom.setAttribute('autocomplete', 'off');
33336         }
33337         this.field.on("specialkey", this.onSpecialKey, this);
33338         if(this.swallowKeys){
33339             this.field.el.swallowEvent(['keydown','keypress']);
33340         }
33341         this.field.show();
33342         this.field.on("blur", this.onBlur, this);
33343         if(this.field.grow){
33344             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33345         }
33346     },
33347
33348     onSpecialKey : function(field, e)
33349     {
33350         //Roo.log('editor onSpecialKey');
33351         if(this.completeOnEnter && e.getKey() == e.ENTER){
33352             e.stopEvent();
33353             this.completeEdit();
33354             return;
33355         }
33356         // do not fire special key otherwise it might hide close the editor...
33357         if(e.getKey() == e.ENTER){    
33358             return;
33359         }
33360         if(this.cancelOnEsc && e.getKey() == e.ESC){
33361             this.cancelEdit();
33362             return;
33363         } 
33364         this.fireEvent('specialkey', field, e);
33365     
33366     },
33367
33368     /**
33369      * Starts the editing process and shows the editor.
33370      * @param {String/HTMLElement/Element} el The element to edit
33371      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33372       * to the innerHTML of el.
33373      */
33374     startEdit : function(el, value){
33375         if(this.editing){
33376             this.completeEdit();
33377         }
33378         this.boundEl = Roo.get(el);
33379         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33380         if(!this.rendered){
33381             this.render(this.parentEl || document.body);
33382         }
33383         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33384             return;
33385         }
33386         this.startValue = v;
33387         this.field.setValue(v);
33388         if(this.autoSize){
33389             var sz = this.boundEl.getSize();
33390             switch(this.autoSize){
33391                 case "width":
33392                 this.setSize(sz.width,  "");
33393                 break;
33394                 case "height":
33395                 this.setSize("",  sz.height);
33396                 break;
33397                 default:
33398                 this.setSize(sz.width,  sz.height);
33399             }
33400         }
33401         this.el.alignTo(this.boundEl, this.alignment);
33402         this.editing = true;
33403         if(Roo.QuickTips){
33404             Roo.QuickTips.disable();
33405         }
33406         this.show();
33407     },
33408
33409     /**
33410      * Sets the height and width of this editor.
33411      * @param {Number} width The new width
33412      * @param {Number} height The new height
33413      */
33414     setSize : function(w, h){
33415         this.field.setSize(w, h);
33416         if(this.el){
33417             this.el.sync();
33418         }
33419     },
33420
33421     /**
33422      * Realigns the editor to the bound field based on the current alignment config value.
33423      */
33424     realign : function(){
33425         this.el.alignTo(this.boundEl, this.alignment);
33426     },
33427
33428     /**
33429      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33430      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33431      */
33432     completeEdit : function(remainVisible){
33433         if(!this.editing){
33434             return;
33435         }
33436         var v = this.getValue();
33437         if(this.revertInvalid !== false && !this.field.isValid()){
33438             v = this.startValue;
33439             this.cancelEdit(true);
33440         }
33441         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33442             this.editing = false;
33443             this.hide();
33444             return;
33445         }
33446         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33447             this.editing = false;
33448             if(this.updateEl && this.boundEl){
33449                 this.boundEl.update(v);
33450             }
33451             if(remainVisible !== true){
33452                 this.hide();
33453             }
33454             this.fireEvent("complete", this, v, this.startValue);
33455         }
33456     },
33457
33458     // private
33459     onShow : function(){
33460         this.el.show();
33461         if(this.hideEl !== false){
33462             this.boundEl.hide();
33463         }
33464         this.field.show();
33465         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33466             this.fixIEFocus = true;
33467             this.deferredFocus.defer(50, this);
33468         }else{
33469             this.field.focus();
33470         }
33471         this.fireEvent("startedit", this.boundEl, this.startValue);
33472     },
33473
33474     deferredFocus : function(){
33475         if(this.editing){
33476             this.field.focus();
33477         }
33478     },
33479
33480     /**
33481      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33482      * reverted to the original starting value.
33483      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33484      * cancel (defaults to false)
33485      */
33486     cancelEdit : function(remainVisible){
33487         if(this.editing){
33488             this.setValue(this.startValue);
33489             if(remainVisible !== true){
33490                 this.hide();
33491             }
33492         }
33493     },
33494
33495     // private
33496     onBlur : function(){
33497         if(this.allowBlur !== true && this.editing){
33498             this.completeEdit();
33499         }
33500     },
33501
33502     // private
33503     onHide : function(){
33504         if(this.editing){
33505             this.completeEdit();
33506             return;
33507         }
33508         this.field.blur();
33509         if(this.field.collapse){
33510             this.field.collapse();
33511         }
33512         this.el.hide();
33513         if(this.hideEl !== false){
33514             this.boundEl.show();
33515         }
33516         if(Roo.QuickTips){
33517             Roo.QuickTips.enable();
33518         }
33519     },
33520
33521     /**
33522      * Sets the data value of the editor
33523      * @param {Mixed} value Any valid value supported by the underlying field
33524      */
33525     setValue : function(v){
33526         this.field.setValue(v);
33527     },
33528
33529     /**
33530      * Gets the data value of the editor
33531      * @return {Mixed} The data value
33532      */
33533     getValue : function(){
33534         return this.field.getValue();
33535     }
33536 });/*
33537  * Based on:
33538  * Ext JS Library 1.1.1
33539  * Copyright(c) 2006-2007, Ext JS, LLC.
33540  *
33541  * Originally Released Under LGPL - original licence link has changed is not relivant.
33542  *
33543  * Fork - LGPL
33544  * <script type="text/javascript">
33545  */
33546  
33547 /**
33548  * @class Roo.BasicDialog
33549  * @extends Roo.util.Observable
33550  * @parent none builder
33551  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33552  * <pre><code>
33553 var dlg = new Roo.BasicDialog("my-dlg", {
33554     height: 200,
33555     width: 300,
33556     minHeight: 100,
33557     minWidth: 150,
33558     modal: true,
33559     proxyDrag: true,
33560     shadow: true
33561 });
33562 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33563 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33564 dlg.addButton('Cancel', dlg.hide, dlg);
33565 dlg.show();
33566 </code></pre>
33567   <b>A Dialog should always be a direct child of the body element.</b>
33568  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33569  * @cfg {String} title Default text to display in the title bar (defaults to null)
33570  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33571  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33572  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33573  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33574  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33575  * (defaults to null with no animation)
33576  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33577  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33578  * property for valid values (defaults to 'all')
33579  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33580  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33581  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33582  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33583  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33584  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33585  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33586  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33587  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33588  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33589  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33590  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33591  * draggable = true (defaults to false)
33592  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33593  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33594  * shadow (defaults to false)
33595  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33596  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33597  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33598  * @cfg {Array} buttons Array of buttons
33599  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33600  * @constructor
33601  * Create a new BasicDialog.
33602  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33603  * @param {Object} config Configuration options
33604  */
33605 Roo.BasicDialog = function(el, config){
33606     this.el = Roo.get(el);
33607     var dh = Roo.DomHelper;
33608     if(!this.el && config && config.autoCreate){
33609         if(typeof config.autoCreate == "object"){
33610             if(!config.autoCreate.id){
33611                 config.autoCreate.id = el;
33612             }
33613             this.el = dh.append(document.body,
33614                         config.autoCreate, true);
33615         }else{
33616             this.el = dh.append(document.body,
33617                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33618         }
33619     }
33620     el = this.el;
33621     el.setDisplayed(true);
33622     el.hide = this.hideAction;
33623     this.id = el.id;
33624     el.addClass("x-dlg");
33625
33626     Roo.apply(this, config);
33627
33628     this.proxy = el.createProxy("x-dlg-proxy");
33629     this.proxy.hide = this.hideAction;
33630     this.proxy.setOpacity(.5);
33631     this.proxy.hide();
33632
33633     if(config.width){
33634         el.setWidth(config.width);
33635     }
33636     if(config.height){
33637         el.setHeight(config.height);
33638     }
33639     this.size = el.getSize();
33640     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33641         this.xy = [config.x,config.y];
33642     }else{
33643         this.xy = el.getCenterXY(true);
33644     }
33645     /** The header element @type Roo.Element */
33646     this.header = el.child("> .x-dlg-hd");
33647     /** The body element @type Roo.Element */
33648     this.body = el.child("> .x-dlg-bd");
33649     /** The footer element @type Roo.Element */
33650     this.footer = el.child("> .x-dlg-ft");
33651
33652     if(!this.header){
33653         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33654     }
33655     if(!this.body){
33656         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33657     }
33658
33659     this.header.unselectable();
33660     if(this.title){
33661         this.header.update(this.title);
33662     }
33663     // this element allows the dialog to be focused for keyboard event
33664     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33665     this.focusEl.swallowEvent("click", true);
33666
33667     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33668
33669     // wrap the body and footer for special rendering
33670     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33671     if(this.footer){
33672         this.bwrap.dom.appendChild(this.footer.dom);
33673     }
33674
33675     this.bg = this.el.createChild({
33676         tag: "div", cls:"x-dlg-bg",
33677         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33678     });
33679     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33680
33681
33682     if(this.autoScroll !== false && !this.autoTabs){
33683         this.body.setStyle("overflow", "auto");
33684     }
33685
33686     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33687
33688     if(this.closable !== false){
33689         this.el.addClass("x-dlg-closable");
33690         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33691         this.close.on("click", this.closeClick, this);
33692         this.close.addClassOnOver("x-dlg-close-over");
33693     }
33694     if(this.collapsible !== false){
33695         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33696         this.collapseBtn.on("click", this.collapseClick, this);
33697         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33698         this.header.on("dblclick", this.collapseClick, this);
33699     }
33700     if(this.resizable !== false){
33701         this.el.addClass("x-dlg-resizable");
33702         this.resizer = new Roo.Resizable(el, {
33703             minWidth: this.minWidth || 80,
33704             minHeight:this.minHeight || 80,
33705             handles: this.resizeHandles || "all",
33706             pinned: true
33707         });
33708         this.resizer.on("beforeresize", this.beforeResize, this);
33709         this.resizer.on("resize", this.onResize, this);
33710     }
33711     if(this.draggable !== false){
33712         el.addClass("x-dlg-draggable");
33713         if (!this.proxyDrag) {
33714             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33715         }
33716         else {
33717             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33718         }
33719         dd.setHandleElId(this.header.id);
33720         dd.endDrag = this.endMove.createDelegate(this);
33721         dd.startDrag = this.startMove.createDelegate(this);
33722         dd.onDrag = this.onDrag.createDelegate(this);
33723         dd.scroll = false;
33724         this.dd = dd;
33725     }
33726     if(this.modal){
33727         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33728         this.mask.enableDisplayMode("block");
33729         this.mask.hide();
33730         this.el.addClass("x-dlg-modal");
33731     }
33732     if(this.shadow){
33733         this.shadow = new Roo.Shadow({
33734             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33735             offset : this.shadowOffset
33736         });
33737     }else{
33738         this.shadowOffset = 0;
33739     }
33740     if(Roo.useShims && this.shim !== false){
33741         this.shim = this.el.createShim();
33742         this.shim.hide = this.hideAction;
33743         this.shim.hide();
33744     }else{
33745         this.shim = false;
33746     }
33747     if(this.autoTabs){
33748         this.initTabs();
33749     }
33750     if (this.buttons) { 
33751         var bts= this.buttons;
33752         this.buttons = [];
33753         Roo.each(bts, function(b) {
33754             this.addButton(b);
33755         }, this);
33756     }
33757     
33758     
33759     this.addEvents({
33760         /**
33761          * @event keydown
33762          * Fires when a key is pressed
33763          * @param {Roo.BasicDialog} this
33764          * @param {Roo.EventObject} e
33765          */
33766         "keydown" : true,
33767         /**
33768          * @event move
33769          * Fires when this dialog is moved by the user.
33770          * @param {Roo.BasicDialog} this
33771          * @param {Number} x The new page X
33772          * @param {Number} y The new page Y
33773          */
33774         "move" : true,
33775         /**
33776          * @event resize
33777          * Fires when this dialog is resized by the user.
33778          * @param {Roo.BasicDialog} this
33779          * @param {Number} width The new width
33780          * @param {Number} height The new height
33781          */
33782         "resize" : true,
33783         /**
33784          * @event beforehide
33785          * Fires before this dialog is hidden.
33786          * @param {Roo.BasicDialog} this
33787          */
33788         "beforehide" : true,
33789         /**
33790          * @event hide
33791          * Fires when this dialog is hidden.
33792          * @param {Roo.BasicDialog} this
33793          */
33794         "hide" : true,
33795         /**
33796          * @event beforeshow
33797          * Fires before this dialog is shown.
33798          * @param {Roo.BasicDialog} this
33799          */
33800         "beforeshow" : true,
33801         /**
33802          * @event show
33803          * Fires when this dialog is shown.
33804          * @param {Roo.BasicDialog} this
33805          */
33806         "show" : true
33807     });
33808     el.on("keydown", this.onKeyDown, this);
33809     el.on("mousedown", this.toFront, this);
33810     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33811     this.el.hide();
33812     Roo.DialogManager.register(this);
33813     Roo.BasicDialog.superclass.constructor.call(this);
33814 };
33815
33816 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33817     shadowOffset: Roo.isIE ? 6 : 5,
33818     minHeight: 80,
33819     minWidth: 200,
33820     minButtonWidth: 75,
33821     defaultButton: null,
33822     buttonAlign: "right",
33823     tabTag: 'div',
33824     firstShow: true,
33825
33826     /**
33827      * Sets the dialog title text
33828      * @param {String} text The title text to display
33829      * @return {Roo.BasicDialog} this
33830      */
33831     setTitle : function(text){
33832         this.header.update(text);
33833         return this;
33834     },
33835
33836     // private
33837     closeClick : function(){
33838         this.hide();
33839     },
33840
33841     // private
33842     collapseClick : function(){
33843         this[this.collapsed ? "expand" : "collapse"]();
33844     },
33845
33846     /**
33847      * Collapses the dialog to its minimized state (only the title bar is visible).
33848      * Equivalent to the user clicking the collapse dialog button.
33849      */
33850     collapse : function(){
33851         if(!this.collapsed){
33852             this.collapsed = true;
33853             this.el.addClass("x-dlg-collapsed");
33854             this.restoreHeight = this.el.getHeight();
33855             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33856         }
33857     },
33858
33859     /**
33860      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33861      * clicking the expand dialog button.
33862      */
33863     expand : function(){
33864         if(this.collapsed){
33865             this.collapsed = false;
33866             this.el.removeClass("x-dlg-collapsed");
33867             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33868         }
33869     },
33870
33871     /**
33872      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33873      * @return {Roo.TabPanel} The tabs component
33874      */
33875     initTabs : function(){
33876         var tabs = this.getTabs();
33877         while(tabs.getTab(0)){
33878             tabs.removeTab(0);
33879         }
33880         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33881             var dom = el.dom;
33882             tabs.addTab(Roo.id(dom), dom.title);
33883             dom.title = "";
33884         });
33885         tabs.activate(0);
33886         return tabs;
33887     },
33888
33889     // private
33890     beforeResize : function(){
33891         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33892     },
33893
33894     // private
33895     onResize : function(){
33896         this.refreshSize();
33897         this.syncBodyHeight();
33898         this.adjustAssets();
33899         this.focus();
33900         this.fireEvent("resize", this, this.size.width, this.size.height);
33901     },
33902
33903     // private
33904     onKeyDown : function(e){
33905         if(this.isVisible()){
33906             this.fireEvent("keydown", this, e);
33907         }
33908     },
33909
33910     /**
33911      * Resizes the dialog.
33912      * @param {Number} width
33913      * @param {Number} height
33914      * @return {Roo.BasicDialog} this
33915      */
33916     resizeTo : function(width, height){
33917         this.el.setSize(width, height);
33918         this.size = {width: width, height: height};
33919         this.syncBodyHeight();
33920         if(this.fixedcenter){
33921             this.center();
33922         }
33923         if(this.isVisible()){
33924             this.constrainXY();
33925             this.adjustAssets();
33926         }
33927         this.fireEvent("resize", this, width, height);
33928         return this;
33929     },
33930
33931
33932     /**
33933      * Resizes the dialog to fit the specified content size.
33934      * @param {Number} width
33935      * @param {Number} height
33936      * @return {Roo.BasicDialog} this
33937      */
33938     setContentSize : function(w, h){
33939         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33940         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33941         //if(!this.el.isBorderBox()){
33942             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33943             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33944         //}
33945         if(this.tabs){
33946             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33947             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33948         }
33949         this.resizeTo(w, h);
33950         return this;
33951     },
33952
33953     /**
33954      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33955      * executed in response to a particular key being pressed while the dialog is active.
33956      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33957      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33958      * @param {Function} fn The function to call
33959      * @param {Object} scope (optional) The scope of the function
33960      * @return {Roo.BasicDialog} this
33961      */
33962     addKeyListener : function(key, fn, scope){
33963         var keyCode, shift, ctrl, alt;
33964         if(typeof key == "object" && !(key instanceof Array)){
33965             keyCode = key["key"];
33966             shift = key["shift"];
33967             ctrl = key["ctrl"];
33968             alt = key["alt"];
33969         }else{
33970             keyCode = key;
33971         }
33972         var handler = function(dlg, e){
33973             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33974                 var k = e.getKey();
33975                 if(keyCode instanceof Array){
33976                     for(var i = 0, len = keyCode.length; i < len; i++){
33977                         if(keyCode[i] == k){
33978                           fn.call(scope || window, dlg, k, e);
33979                           return;
33980                         }
33981                     }
33982                 }else{
33983                     if(k == keyCode){
33984                         fn.call(scope || window, dlg, k, e);
33985                     }
33986                 }
33987             }
33988         };
33989         this.on("keydown", handler);
33990         return this;
33991     },
33992
33993     /**
33994      * Returns the TabPanel component (creates it if it doesn't exist).
33995      * Note: If you wish to simply check for the existence of tabs without creating them,
33996      * check for a null 'tabs' property.
33997      * @return {Roo.TabPanel} The tabs component
33998      */
33999     getTabs : function(){
34000         if(!this.tabs){
34001             this.el.addClass("x-dlg-auto-tabs");
34002             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
34003             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
34004         }
34005         return this.tabs;
34006     },
34007
34008     /**
34009      * Adds a button to the footer section of the dialog.
34010      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
34011      * object or a valid Roo.DomHelper element config
34012      * @param {Function} handler The function called when the button is clicked
34013      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
34014      * @return {Roo.Button} The new button
34015      */
34016     addButton : function(config, handler, scope){
34017         var dh = Roo.DomHelper;
34018         if(!this.footer){
34019             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
34020         }
34021         if(!this.btnContainer){
34022             var tb = this.footer.createChild({
34023
34024                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
34025                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34026             }, null, true);
34027             this.btnContainer = tb.firstChild.firstChild.firstChild;
34028         }
34029         var bconfig = {
34030             handler: handler,
34031             scope: scope,
34032             minWidth: this.minButtonWidth,
34033             hideParent:true
34034         };
34035         if(typeof config == "string"){
34036             bconfig.text = config;
34037         }else{
34038             if(config.tag){
34039                 bconfig.dhconfig = config;
34040             }else{
34041                 Roo.apply(bconfig, config);
34042             }
34043         }
34044         var fc = false;
34045         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34046             bconfig.position = Math.max(0, bconfig.position);
34047             fc = this.btnContainer.childNodes[bconfig.position];
34048         }
34049          
34050         var btn = new Roo.Button(
34051             fc ? 
34052                 this.btnContainer.insertBefore(document.createElement("td"),fc)
34053                 : this.btnContainer.appendChild(document.createElement("td")),
34054             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
34055             bconfig
34056         );
34057         this.syncBodyHeight();
34058         if(!this.buttons){
34059             /**
34060              * Array of all the buttons that have been added to this dialog via addButton
34061              * @type Array
34062              */
34063             this.buttons = [];
34064         }
34065         this.buttons.push(btn);
34066         return btn;
34067     },
34068
34069     /**
34070      * Sets the default button to be focused when the dialog is displayed.
34071      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34072      * @return {Roo.BasicDialog} this
34073      */
34074     setDefaultButton : function(btn){
34075         this.defaultButton = btn;
34076         return this;
34077     },
34078
34079     // private
34080     getHeaderFooterHeight : function(safe){
34081         var height = 0;
34082         if(this.header){
34083            height += this.header.getHeight();
34084         }
34085         if(this.footer){
34086            var fm = this.footer.getMargins();
34087             height += (this.footer.getHeight()+fm.top+fm.bottom);
34088         }
34089         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34090         height += this.centerBg.getPadding("tb");
34091         return height;
34092     },
34093
34094     // private
34095     syncBodyHeight : function()
34096     {
34097         var bd = this.body, // the text
34098             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34099             bw = this.bwrap;
34100         var height = this.size.height - this.getHeaderFooterHeight(false);
34101         bd.setHeight(height-bd.getMargins("tb"));
34102         var hh = this.header.getHeight();
34103         var h = this.size.height-hh;
34104         cb.setHeight(h);
34105         
34106         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34107         bw.setHeight(h-cb.getPadding("tb"));
34108         
34109         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34110         bd.setWidth(bw.getWidth(true));
34111         if(this.tabs){
34112             this.tabs.syncHeight();
34113             if(Roo.isIE){
34114                 this.tabs.el.repaint();
34115             }
34116         }
34117     },
34118
34119     /**
34120      * Restores the previous state of the dialog if Roo.state is configured.
34121      * @return {Roo.BasicDialog} this
34122      */
34123     restoreState : function(){
34124         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34125         if(box && box.width){
34126             this.xy = [box.x, box.y];
34127             this.resizeTo(box.width, box.height);
34128         }
34129         return this;
34130     },
34131
34132     // private
34133     beforeShow : function(){
34134         this.expand();
34135         if(this.fixedcenter){
34136             this.xy = this.el.getCenterXY(true);
34137         }
34138         if(this.modal){
34139             Roo.get(document.body).addClass("x-body-masked");
34140             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34141             this.mask.show();
34142         }
34143         this.constrainXY();
34144     },
34145
34146     // private
34147     animShow : function(){
34148         var b = Roo.get(this.animateTarget).getBox();
34149         this.proxy.setSize(b.width, b.height);
34150         this.proxy.setLocation(b.x, b.y);
34151         this.proxy.show();
34152         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34153                     true, .35, this.showEl.createDelegate(this));
34154     },
34155
34156     /**
34157      * Shows the dialog.
34158      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34159      * @return {Roo.BasicDialog} this
34160      */
34161     show : function(animateTarget){
34162         if (this.fireEvent("beforeshow", this) === false){
34163             return;
34164         }
34165         if(this.syncHeightBeforeShow){
34166             this.syncBodyHeight();
34167         }else if(this.firstShow){
34168             this.firstShow = false;
34169             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34170         }
34171         this.animateTarget = animateTarget || this.animateTarget;
34172         if(!this.el.isVisible()){
34173             this.beforeShow();
34174             if(this.animateTarget && Roo.get(this.animateTarget)){
34175                 this.animShow();
34176             }else{
34177                 this.showEl();
34178             }
34179         }
34180         return this;
34181     },
34182
34183     // private
34184     showEl : function(){
34185         this.proxy.hide();
34186         this.el.setXY(this.xy);
34187         this.el.show();
34188         this.adjustAssets(true);
34189         this.toFront();
34190         this.focus();
34191         // IE peekaboo bug - fix found by Dave Fenwick
34192         if(Roo.isIE){
34193             this.el.repaint();
34194         }
34195         this.fireEvent("show", this);
34196     },
34197
34198     /**
34199      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
34200      * dialog itself will receive focus.
34201      */
34202     focus : function(){
34203         if(this.defaultButton){
34204             this.defaultButton.focus();
34205         }else{
34206             this.focusEl.focus();
34207         }
34208     },
34209
34210     // private
34211     constrainXY : function(){
34212         if(this.constraintoviewport !== false){
34213             if(!this.viewSize){
34214                 if(this.container){
34215                     var s = this.container.getSize();
34216                     this.viewSize = [s.width, s.height];
34217                 }else{
34218                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34219                 }
34220             }
34221             var s = Roo.get(this.container||document).getScroll();
34222
34223             var x = this.xy[0], y = this.xy[1];
34224             var w = this.size.width, h = this.size.height;
34225             var vw = this.viewSize[0], vh = this.viewSize[1];
34226             // only move it if it needs it
34227             var moved = false;
34228             // first validate right/bottom
34229             if(x + w > vw+s.left){
34230                 x = vw - w;
34231                 moved = true;
34232             }
34233             if(y + h > vh+s.top){
34234                 y = vh - h;
34235                 moved = true;
34236             }
34237             // then make sure top/left isn't negative
34238             if(x < s.left){
34239                 x = s.left;
34240                 moved = true;
34241             }
34242             if(y < s.top){
34243                 y = s.top;
34244                 moved = true;
34245             }
34246             if(moved){
34247                 // cache xy
34248                 this.xy = [x, y];
34249                 if(this.isVisible()){
34250                     this.el.setLocation(x, y);
34251                     this.adjustAssets();
34252                 }
34253             }
34254         }
34255     },
34256
34257     // private
34258     onDrag : function(){
34259         if(!this.proxyDrag){
34260             this.xy = this.el.getXY();
34261             this.adjustAssets();
34262         }
34263     },
34264
34265     // private
34266     adjustAssets : function(doShow){
34267         var x = this.xy[0], y = this.xy[1];
34268         var w = this.size.width, h = this.size.height;
34269         if(doShow === true){
34270             if(this.shadow){
34271                 this.shadow.show(this.el);
34272             }
34273             if(this.shim){
34274                 this.shim.show();
34275             }
34276         }
34277         if(this.shadow && this.shadow.isVisible()){
34278             this.shadow.show(this.el);
34279         }
34280         if(this.shim && this.shim.isVisible()){
34281             this.shim.setBounds(x, y, w, h);
34282         }
34283     },
34284
34285     // private
34286     adjustViewport : function(w, h){
34287         if(!w || !h){
34288             w = Roo.lib.Dom.getViewWidth();
34289             h = Roo.lib.Dom.getViewHeight();
34290         }
34291         // cache the size
34292         this.viewSize = [w, h];
34293         if(this.modal && this.mask.isVisible()){
34294             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34295             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34296         }
34297         if(this.isVisible()){
34298             this.constrainXY();
34299         }
34300     },
34301
34302     /**
34303      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34304      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34305      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34306      */
34307     destroy : function(removeEl){
34308         if(this.isVisible()){
34309             this.animateTarget = null;
34310             this.hide();
34311         }
34312         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34313         if(this.tabs){
34314             this.tabs.destroy(removeEl);
34315         }
34316         Roo.destroy(
34317              this.shim,
34318              this.proxy,
34319              this.resizer,
34320              this.close,
34321              this.mask
34322         );
34323         if(this.dd){
34324             this.dd.unreg();
34325         }
34326         if(this.buttons){
34327            for(var i = 0, len = this.buttons.length; i < len; i++){
34328                this.buttons[i].destroy();
34329            }
34330         }
34331         this.el.removeAllListeners();
34332         if(removeEl === true){
34333             this.el.update("");
34334             this.el.remove();
34335         }
34336         Roo.DialogManager.unregister(this);
34337     },
34338
34339     // private
34340     startMove : function(){
34341         if(this.proxyDrag){
34342             this.proxy.show();
34343         }
34344         if(this.constraintoviewport !== false){
34345             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34346         }
34347     },
34348
34349     // private
34350     endMove : function(){
34351         if(!this.proxyDrag){
34352             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34353         }else{
34354             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34355             this.proxy.hide();
34356         }
34357         this.refreshSize();
34358         this.adjustAssets();
34359         this.focus();
34360         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34361     },
34362
34363     /**
34364      * Brings this dialog to the front of any other visible dialogs
34365      * @return {Roo.BasicDialog} this
34366      */
34367     toFront : function(){
34368         Roo.DialogManager.bringToFront(this);
34369         return this;
34370     },
34371
34372     /**
34373      * Sends this dialog to the back (under) of any other visible dialogs
34374      * @return {Roo.BasicDialog} this
34375      */
34376     toBack : function(){
34377         Roo.DialogManager.sendToBack(this);
34378         return this;
34379     },
34380
34381     /**
34382      * Centers this dialog in the viewport
34383      * @return {Roo.BasicDialog} this
34384      */
34385     center : function(){
34386         var xy = this.el.getCenterXY(true);
34387         this.moveTo(xy[0], xy[1]);
34388         return this;
34389     },
34390
34391     /**
34392      * Moves the dialog's top-left corner to the specified point
34393      * @param {Number} x
34394      * @param {Number} y
34395      * @return {Roo.BasicDialog} this
34396      */
34397     moveTo : function(x, y){
34398         this.xy = [x,y];
34399         if(this.isVisible()){
34400             this.el.setXY(this.xy);
34401             this.adjustAssets();
34402         }
34403         return this;
34404     },
34405
34406     /**
34407      * Aligns the dialog to the specified element
34408      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34409      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34410      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34411      * @return {Roo.BasicDialog} this
34412      */
34413     alignTo : function(element, position, offsets){
34414         this.xy = this.el.getAlignToXY(element, position, offsets);
34415         if(this.isVisible()){
34416             this.el.setXY(this.xy);
34417             this.adjustAssets();
34418         }
34419         return this;
34420     },
34421
34422     /**
34423      * Anchors an element to another element and realigns it when the window is resized.
34424      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34425      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34426      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34427      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34428      * is a number, it is used as the buffer delay (defaults to 50ms).
34429      * @return {Roo.BasicDialog} this
34430      */
34431     anchorTo : function(el, alignment, offsets, monitorScroll){
34432         var action = function(){
34433             this.alignTo(el, alignment, offsets);
34434         };
34435         Roo.EventManager.onWindowResize(action, this);
34436         var tm = typeof monitorScroll;
34437         if(tm != 'undefined'){
34438             Roo.EventManager.on(window, 'scroll', action, this,
34439                 {buffer: tm == 'number' ? monitorScroll : 50});
34440         }
34441         action.call(this);
34442         return this;
34443     },
34444
34445     /**
34446      * Returns true if the dialog is visible
34447      * @return {Boolean}
34448      */
34449     isVisible : function(){
34450         return this.el.isVisible();
34451     },
34452
34453     // private
34454     animHide : function(callback){
34455         var b = Roo.get(this.animateTarget).getBox();
34456         this.proxy.show();
34457         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34458         this.el.hide();
34459         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34460                     this.hideEl.createDelegate(this, [callback]));
34461     },
34462
34463     /**
34464      * Hides the dialog.
34465      * @param {Function} callback (optional) Function to call when the dialog is hidden
34466      * @return {Roo.BasicDialog} this
34467      */
34468     hide : function(callback){
34469         if (this.fireEvent("beforehide", this) === false){
34470             return;
34471         }
34472         if(this.shadow){
34473             this.shadow.hide();
34474         }
34475         if(this.shim) {
34476           this.shim.hide();
34477         }
34478         // sometimes animateTarget seems to get set.. causing problems...
34479         // this just double checks..
34480         if(this.animateTarget && Roo.get(this.animateTarget)) {
34481            this.animHide(callback);
34482         }else{
34483             this.el.hide();
34484             this.hideEl(callback);
34485         }
34486         return this;
34487     },
34488
34489     // private
34490     hideEl : function(callback){
34491         this.proxy.hide();
34492         if(this.modal){
34493             this.mask.hide();
34494             Roo.get(document.body).removeClass("x-body-masked");
34495         }
34496         this.fireEvent("hide", this);
34497         if(typeof callback == "function"){
34498             callback();
34499         }
34500     },
34501
34502     // private
34503     hideAction : function(){
34504         this.setLeft("-10000px");
34505         this.setTop("-10000px");
34506         this.setStyle("visibility", "hidden");
34507     },
34508
34509     // private
34510     refreshSize : function(){
34511         this.size = this.el.getSize();
34512         this.xy = this.el.getXY();
34513         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34514     },
34515
34516     // private
34517     // z-index is managed by the DialogManager and may be overwritten at any time
34518     setZIndex : function(index){
34519         if(this.modal){
34520             this.mask.setStyle("z-index", index);
34521         }
34522         if(this.shim){
34523             this.shim.setStyle("z-index", ++index);
34524         }
34525         if(this.shadow){
34526             this.shadow.setZIndex(++index);
34527         }
34528         this.el.setStyle("z-index", ++index);
34529         if(this.proxy){
34530             this.proxy.setStyle("z-index", ++index);
34531         }
34532         if(this.resizer){
34533             this.resizer.proxy.setStyle("z-index", ++index);
34534         }
34535
34536         this.lastZIndex = index;
34537     },
34538
34539     /**
34540      * Returns the element for this dialog
34541      * @return {Roo.Element} The underlying dialog Element
34542      */
34543     getEl : function(){
34544         return this.el;
34545     }
34546 });
34547
34548 /**
34549  * @class Roo.DialogManager
34550  * Provides global access to BasicDialogs that have been created and
34551  * support for z-indexing (layering) multiple open dialogs.
34552  */
34553 Roo.DialogManager = function(){
34554     var list = {};
34555     var accessList = [];
34556     var front = null;
34557
34558     // private
34559     var sortDialogs = function(d1, d2){
34560         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34561     };
34562
34563     // private
34564     var orderDialogs = function(){
34565         accessList.sort(sortDialogs);
34566         var seed = Roo.DialogManager.zseed;
34567         for(var i = 0, len = accessList.length; i < len; i++){
34568             var dlg = accessList[i];
34569             if(dlg){
34570                 dlg.setZIndex(seed + (i*10));
34571             }
34572         }
34573     };
34574
34575     return {
34576         /**
34577          * The starting z-index for BasicDialogs (defaults to 9000)
34578          * @type Number The z-index value
34579          */
34580         zseed : 9000,
34581
34582         // private
34583         register : function(dlg){
34584             list[dlg.id] = dlg;
34585             accessList.push(dlg);
34586         },
34587
34588         // private
34589         unregister : function(dlg){
34590             delete list[dlg.id];
34591             var i=0;
34592             var len=0;
34593             if(!accessList.indexOf){
34594                 for(  i = 0, len = accessList.length; i < len; i++){
34595                     if(accessList[i] == dlg){
34596                         accessList.splice(i, 1);
34597                         return;
34598                     }
34599                 }
34600             }else{
34601                  i = accessList.indexOf(dlg);
34602                 if(i != -1){
34603                     accessList.splice(i, 1);
34604                 }
34605             }
34606         },
34607
34608         /**
34609          * Gets a registered dialog by id
34610          * @param {String/Object} id The id of the dialog or a dialog
34611          * @return {Roo.BasicDialog} this
34612          */
34613         get : function(id){
34614             return typeof id == "object" ? id : list[id];
34615         },
34616
34617         /**
34618          * Brings the specified dialog to the front
34619          * @param {String/Object} dlg The id of the dialog or a dialog
34620          * @return {Roo.BasicDialog} this
34621          */
34622         bringToFront : function(dlg){
34623             dlg = this.get(dlg);
34624             if(dlg != front){
34625                 front = dlg;
34626                 dlg._lastAccess = new Date().getTime();
34627                 orderDialogs();
34628             }
34629             return dlg;
34630         },
34631
34632         /**
34633          * Sends the specified dialog to the back
34634          * @param {String/Object} dlg The id of the dialog or a dialog
34635          * @return {Roo.BasicDialog} this
34636          */
34637         sendToBack : function(dlg){
34638             dlg = this.get(dlg);
34639             dlg._lastAccess = -(new Date().getTime());
34640             orderDialogs();
34641             return dlg;
34642         },
34643
34644         /**
34645          * Hides all dialogs
34646          */
34647         hideAll : function(){
34648             for(var id in list){
34649                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34650                     list[id].hide();
34651                 }
34652             }
34653         }
34654     };
34655 }();
34656
34657 /**
34658  * @class Roo.LayoutDialog
34659  * @extends Roo.BasicDialog
34660  * @children Roo.ContentPanel
34661  * @parent builder none
34662  * Dialog which provides adjustments for working with a layout in a Dialog.
34663  * Add your necessary layout config options to the dialog's config.<br>
34664  * Example usage (including a nested layout):
34665  * <pre><code>
34666 if(!dialog){
34667     dialog = new Roo.LayoutDialog("download-dlg", {
34668         modal: true,
34669         width:600,
34670         height:450,
34671         shadow:true,
34672         minWidth:500,
34673         minHeight:350,
34674         autoTabs:true,
34675         proxyDrag:true,
34676         // layout config merges with the dialog config
34677         center:{
34678             tabPosition: "top",
34679             alwaysShowTabs: true
34680         }
34681     });
34682     dialog.addKeyListener(27, dialog.hide, dialog);
34683     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34684     dialog.addButton("Build It!", this.getDownload, this);
34685
34686     // we can even add nested layouts
34687     var innerLayout = new Roo.BorderLayout("dl-inner", {
34688         east: {
34689             initialSize: 200,
34690             autoScroll:true,
34691             split:true
34692         },
34693         center: {
34694             autoScroll:true
34695         }
34696     });
34697     innerLayout.beginUpdate();
34698     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34699     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34700     innerLayout.endUpdate(true);
34701
34702     var layout = dialog.getLayout();
34703     layout.beginUpdate();
34704     layout.add("center", new Roo.ContentPanel("standard-panel",
34705                         {title: "Download the Source", fitToFrame:true}));
34706     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34707                {title: "Build your own roo.js"}));
34708     layout.getRegion("center").showPanel(sp);
34709     layout.endUpdate();
34710 }
34711 </code></pre>
34712     * @constructor
34713     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34714     * @param {Object} config configuration options
34715   */
34716 Roo.LayoutDialog = function(el, cfg){
34717     
34718     var config=  cfg;
34719     if (typeof(cfg) == 'undefined') {
34720         config = Roo.apply({}, el);
34721         // not sure why we use documentElement here.. - it should always be body.
34722         // IE7 borks horribly if we use documentElement.
34723         // webkit also does not like documentElement - it creates a body element...
34724         el = Roo.get( document.body || document.documentElement ).createChild();
34725         //config.autoCreate = true;
34726     }
34727     
34728     
34729     config.autoTabs = false;
34730     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34731     this.body.setStyle({overflow:"hidden", position:"relative"});
34732     this.layout = new Roo.BorderLayout(this.body.dom, config);
34733     this.layout.monitorWindowResize = false;
34734     this.el.addClass("x-dlg-auto-layout");
34735     // fix case when center region overwrites center function
34736     this.center = Roo.BasicDialog.prototype.center;
34737     this.on("show", this.layout.layout, this.layout, true);
34738     if (config.items) {
34739         var xitems = config.items;
34740         delete config.items;
34741         Roo.each(xitems, this.addxtype, this);
34742     }
34743     
34744     
34745 };
34746 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34747     
34748     
34749     /**
34750      * @cfg {Roo.LayoutRegion} east  
34751      */
34752     /**
34753      * @cfg {Roo.LayoutRegion} west
34754      */
34755     /**
34756      * @cfg {Roo.LayoutRegion} south
34757      */
34758     /**
34759      * @cfg {Roo.LayoutRegion} north
34760      */
34761     /**
34762      * @cfg {Roo.LayoutRegion} center
34763      */
34764     /**
34765      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34766      */
34767     
34768     
34769     /**
34770      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34771      * @deprecated
34772      */
34773     endUpdate : function(){
34774         this.layout.endUpdate();
34775     },
34776
34777     /**
34778      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34779      *  @deprecated
34780      */
34781     beginUpdate : function(){
34782         this.layout.beginUpdate();
34783     },
34784
34785     /**
34786      * Get the BorderLayout for this dialog
34787      * @return {Roo.BorderLayout}
34788      */
34789     getLayout : function(){
34790         return this.layout;
34791     },
34792
34793     showEl : function(){
34794         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34795         if(Roo.isIE7){
34796             this.layout.layout();
34797         }
34798     },
34799
34800     // private
34801     // Use the syncHeightBeforeShow config option to control this automatically
34802     syncBodyHeight : function(){
34803         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34804         if(this.layout){this.layout.layout();}
34805     },
34806     
34807       /**
34808      * Add an xtype element (actually adds to the layout.)
34809      * @return {Object} xdata xtype object data.
34810      */
34811     
34812     addxtype : function(c) {
34813         return this.layout.addxtype(c);
34814     }
34815 });/*
34816  * Based on:
34817  * Ext JS Library 1.1.1
34818  * Copyright(c) 2006-2007, Ext JS, LLC.
34819  *
34820  * Originally Released Under LGPL - original licence link has changed is not relivant.
34821  *
34822  * Fork - LGPL
34823  * <script type="text/javascript">
34824  */
34825  
34826 /**
34827  * @class Roo.MessageBox
34828  * @static
34829  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34830  * Example usage:
34831  *<pre><code>
34832 // Basic alert:
34833 Roo.Msg.alert('Status', 'Changes saved successfully.');
34834
34835 // Prompt for user data:
34836 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34837     if (btn == 'ok'){
34838         // process text value...
34839     }
34840 });
34841
34842 // Show a dialog using config options:
34843 Roo.Msg.show({
34844    title:'Save Changes?',
34845    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34846    buttons: Roo.Msg.YESNOCANCEL,
34847    fn: processResult,
34848    animEl: 'elId'
34849 });
34850 </code></pre>
34851  * @static
34852  */
34853 Roo.MessageBox = function(){
34854     var dlg, opt, mask, waitTimer;
34855     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34856     var buttons, activeTextEl, bwidth;
34857
34858     // private
34859     var handleButton = function(button){
34860         dlg.hide();
34861         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34862     };
34863
34864     // private
34865     var handleHide = function(){
34866         if(opt && opt.cls){
34867             dlg.el.removeClass(opt.cls);
34868         }
34869         if(waitTimer){
34870             Roo.TaskMgr.stop(waitTimer);
34871             waitTimer = null;
34872         }
34873     };
34874
34875     // private
34876     var updateButtons = function(b){
34877         var width = 0;
34878         if(!b){
34879             buttons["ok"].hide();
34880             buttons["cancel"].hide();
34881             buttons["yes"].hide();
34882             buttons["no"].hide();
34883             dlg.footer.dom.style.display = 'none';
34884             return width;
34885         }
34886         dlg.footer.dom.style.display = '';
34887         for(var k in buttons){
34888             if(typeof buttons[k] != "function"){
34889                 if(b[k]){
34890                     buttons[k].show();
34891                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34892                     width += buttons[k].el.getWidth()+15;
34893                 }else{
34894                     buttons[k].hide();
34895                 }
34896             }
34897         }
34898         return width;
34899     };
34900
34901     // private
34902     var handleEsc = function(d, k, e){
34903         if(opt && opt.closable !== false){
34904             dlg.hide();
34905         }
34906         if(e){
34907             e.stopEvent();
34908         }
34909     };
34910
34911     return {
34912         /**
34913          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34914          * @return {Roo.BasicDialog} The BasicDialog element
34915          */
34916         getDialog : function(){
34917            if(!dlg){
34918                 dlg = new Roo.BasicDialog("x-msg-box", {
34919                     autoCreate : true,
34920                     shadow: true,
34921                     draggable: true,
34922                     resizable:false,
34923                     constraintoviewport:false,
34924                     fixedcenter:true,
34925                     collapsible : false,
34926                     shim:true,
34927                     modal: true,
34928                     width:400, height:100,
34929                     buttonAlign:"center",
34930                     closeClick : function(){
34931                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34932                             handleButton("no");
34933                         }else{
34934                             handleButton("cancel");
34935                         }
34936                     }
34937                 });
34938               
34939                 dlg.on("hide", handleHide);
34940                 mask = dlg.mask;
34941                 dlg.addKeyListener(27, handleEsc);
34942                 buttons = {};
34943                 var bt = this.buttonText;
34944                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34945                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34946                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34947                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34948                 bodyEl = dlg.body.createChild({
34949
34950                     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>'
34951                 });
34952                 msgEl = bodyEl.dom.firstChild;
34953                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34954                 textboxEl.enableDisplayMode();
34955                 textboxEl.addKeyListener([10,13], function(){
34956                     if(dlg.isVisible() && opt && opt.buttons){
34957                         if(opt.buttons.ok){
34958                             handleButton("ok");
34959                         }else if(opt.buttons.yes){
34960                             handleButton("yes");
34961                         }
34962                     }
34963                 });
34964                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34965                 textareaEl.enableDisplayMode();
34966                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34967                 progressEl.enableDisplayMode();
34968                 var pf = progressEl.dom.firstChild;
34969                 if (pf) {
34970                     pp = Roo.get(pf.firstChild);
34971                     pp.setHeight(pf.offsetHeight);
34972                 }
34973                 
34974             }
34975             return dlg;
34976         },
34977
34978         /**
34979          * Updates the message box body text
34980          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34981          * the XHTML-compliant non-breaking space character '&amp;#160;')
34982          * @return {Roo.MessageBox} This message box
34983          */
34984         updateText : function(text){
34985             if(!dlg.isVisible() && !opt.width){
34986                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34987             }
34988             msgEl.innerHTML = text || '&#160;';
34989       
34990             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34991             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34992             var w = Math.max(
34993                     Math.min(opt.width || cw , this.maxWidth), 
34994                     Math.max(opt.minWidth || this.minWidth, bwidth)
34995             );
34996             if(opt.prompt){
34997                 activeTextEl.setWidth(w);
34998             }
34999             if(dlg.isVisible()){
35000                 dlg.fixedcenter = false;
35001             }
35002             // to big, make it scroll. = But as usual stupid IE does not support
35003             // !important..
35004             
35005             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
35006                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
35007                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
35008             } else {
35009                 bodyEl.dom.style.height = '';
35010                 bodyEl.dom.style.overflowY = '';
35011             }
35012             if (cw > w) {
35013                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
35014             } else {
35015                 bodyEl.dom.style.overflowX = '';
35016             }
35017             
35018             dlg.setContentSize(w, bodyEl.getHeight());
35019             if(dlg.isVisible()){
35020                 dlg.fixedcenter = true;
35021             }
35022             return this;
35023         },
35024
35025         /**
35026          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
35027          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35028          * @param {Number} value Any number between 0 and 1 (e.g., .5)
35029          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35030          * @return {Roo.MessageBox} This message box
35031          */
35032         updateProgress : function(value, text){
35033             if(text){
35034                 this.updateText(text);
35035             }
35036             if (pp) { // weird bug on my firefox - for some reason this is not defined
35037                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35038             }
35039             return this;
35040         },        
35041
35042         /**
35043          * Returns true if the message box is currently displayed
35044          * @return {Boolean} True if the message box is visible, else false
35045          */
35046         isVisible : function(){
35047             return dlg && dlg.isVisible();  
35048         },
35049
35050         /**
35051          * Hides the message box if it is displayed
35052          */
35053         hide : function(){
35054             if(this.isVisible()){
35055                 dlg.hide();
35056             }  
35057         },
35058
35059         /**
35060          * Displays a new message box, or reinitializes an existing message box, based on the config options
35061          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35062          * The following config object properties are supported:
35063          * <pre>
35064 Property    Type             Description
35065 ----------  ---------------  ------------------------------------------------------------------------------------
35066 animEl            String/Element   An id or Element from which the message box should animate as it opens and
35067                                    closes (defaults to undefined)
35068 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35069                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
35070 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
35071                                    progress and wait dialogs will ignore this property and always hide the
35072                                    close button as they can only be closed programmatically.
35073 cls               String           A custom CSS class to apply to the message box element
35074 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
35075                                    displayed (defaults to 75)
35076 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
35077                                    function will be btn (the name of the button that was clicked, if applicable,
35078                                    e.g. "ok"), and text (the value of the active text field, if applicable).
35079                                    Progress and wait dialogs will ignore this option since they do not respond to
35080                                    user actions and can only be closed programmatically, so any required function
35081                                    should be called by the same code after it closes the dialog.
35082 icon              String           A CSS class that provides a background image to be used as an icon for
35083                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35084 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
35085 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
35086 modal             Boolean          False to allow user interaction with the page while the message box is
35087                                    displayed (defaults to true)
35088 msg               String           A string that will replace the existing message box body text (defaults
35089                                    to the XHTML-compliant non-breaking space character '&#160;')
35090 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
35091 progress          Boolean          True to display a progress bar (defaults to false)
35092 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
35093 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
35094 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
35095 title             String           The title text
35096 value             String           The string value to set into the active textbox element if displayed
35097 wait              Boolean          True to display a progress bar (defaults to false)
35098 width             Number           The width of the dialog in pixels
35099 </pre>
35100          *
35101          * Example usage:
35102          * <pre><code>
35103 Roo.Msg.show({
35104    title: 'Address',
35105    msg: 'Please enter your address:',
35106    width: 300,
35107    buttons: Roo.MessageBox.OKCANCEL,
35108    multiline: true,
35109    fn: saveAddress,
35110    animEl: 'addAddressBtn'
35111 });
35112 </code></pre>
35113          * @param {Object} config Configuration options
35114          * @return {Roo.MessageBox} This message box
35115          */
35116         show : function(options)
35117         {
35118             
35119             // this causes nightmares if you show one dialog after another
35120             // especially on callbacks..
35121              
35122             if(this.isVisible()){
35123                 
35124                 this.hide();
35125                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35126                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
35127                 Roo.log("New Dialog Message:" +  options.msg )
35128                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35129                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35130                 
35131             }
35132             var d = this.getDialog();
35133             opt = options;
35134             d.setTitle(opt.title || "&#160;");
35135             d.close.setDisplayed(opt.closable !== false);
35136             activeTextEl = textboxEl;
35137             opt.prompt = opt.prompt || (opt.multiline ? true : false);
35138             if(opt.prompt){
35139                 if(opt.multiline){
35140                     textboxEl.hide();
35141                     textareaEl.show();
35142                     textareaEl.setHeight(typeof opt.multiline == "number" ?
35143                         opt.multiline : this.defaultTextHeight);
35144                     activeTextEl = textareaEl;
35145                 }else{
35146                     textboxEl.show();
35147                     textareaEl.hide();
35148                 }
35149             }else{
35150                 textboxEl.hide();
35151                 textareaEl.hide();
35152             }
35153             progressEl.setDisplayed(opt.progress === true);
35154             this.updateProgress(0);
35155             activeTextEl.dom.value = opt.value || "";
35156             if(opt.prompt){
35157                 dlg.setDefaultButton(activeTextEl);
35158             }else{
35159                 var bs = opt.buttons;
35160                 var db = null;
35161                 if(bs && bs.ok){
35162                     db = buttons["ok"];
35163                 }else if(bs && bs.yes){
35164                     db = buttons["yes"];
35165                 }
35166                 dlg.setDefaultButton(db);
35167             }
35168             bwidth = updateButtons(opt.buttons);
35169             this.updateText(opt.msg);
35170             if(opt.cls){
35171                 d.el.addClass(opt.cls);
35172             }
35173             d.proxyDrag = opt.proxyDrag === true;
35174             d.modal = opt.modal !== false;
35175             d.mask = opt.modal !== false ? mask : false;
35176             if(!d.isVisible()){
35177                 // force it to the end of the z-index stack so it gets a cursor in FF
35178                 document.body.appendChild(dlg.el.dom);
35179                 d.animateTarget = null;
35180                 d.show(options.animEl);
35181             }
35182             dlg.toFront();
35183             return this;
35184         },
35185
35186         /**
35187          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
35188          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35189          * and closing the message box when the process is complete.
35190          * @param {String} title The title bar text
35191          * @param {String} msg The message box body text
35192          * @return {Roo.MessageBox} This message box
35193          */
35194         progress : function(title, msg){
35195             this.show({
35196                 title : title,
35197                 msg : msg,
35198                 buttons: false,
35199                 progress:true,
35200                 closable:false,
35201                 minWidth: this.minProgressWidth,
35202                 modal : true
35203             });
35204             return this;
35205         },
35206
35207         /**
35208          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35209          * If a callback function is passed it will be called after the user clicks the button, and the
35210          * id of the button that was clicked will be passed as the only parameter to the callback
35211          * (could also be the top-right close button).
35212          * @param {String} title The title bar text
35213          * @param {String} msg The message box body text
35214          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35215          * @param {Object} scope (optional) The scope of the callback function
35216          * @return {Roo.MessageBox} This message box
35217          */
35218         alert : function(title, msg, fn, scope){
35219             this.show({
35220                 title : title,
35221                 msg : msg,
35222                 buttons: this.OK,
35223                 fn: fn,
35224                 scope : scope,
35225                 modal : true
35226             });
35227             return this;
35228         },
35229
35230         /**
35231          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35232          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35233          * You are responsible for closing the message box when the process is complete.
35234          * @param {String} msg The message box body text
35235          * @param {String} title (optional) The title bar text
35236          * @return {Roo.MessageBox} This message box
35237          */
35238         wait : function(msg, title){
35239             this.show({
35240                 title : title,
35241                 msg : msg,
35242                 buttons: false,
35243                 closable:false,
35244                 progress:true,
35245                 modal:true,
35246                 width:300,
35247                 wait:true
35248             });
35249             waitTimer = Roo.TaskMgr.start({
35250                 run: function(i){
35251                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35252                 },
35253                 interval: 1000
35254             });
35255             return this;
35256         },
35257
35258         /**
35259          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35260          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35261          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35262          * @param {String} title The title bar text
35263          * @param {String} msg The message box body text
35264          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35265          * @param {Object} scope (optional) The scope of the callback function
35266          * @return {Roo.MessageBox} This message box
35267          */
35268         confirm : function(title, msg, fn, scope){
35269             this.show({
35270                 title : title,
35271                 msg : msg,
35272                 buttons: this.YESNO,
35273                 fn: fn,
35274                 scope : scope,
35275                 modal : true
35276             });
35277             return this;
35278         },
35279
35280         /**
35281          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35282          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35283          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35284          * (could also be the top-right close button) and the text that was entered will be passed as the two
35285          * parameters to the callback.
35286          * @param {String} title The title bar text
35287          * @param {String} msg The message box body text
35288          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35289          * @param {Object} scope (optional) The scope of the callback function
35290          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35291          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35292          * @return {Roo.MessageBox} This message box
35293          */
35294         prompt : function(title, msg, fn, scope, multiline){
35295             this.show({
35296                 title : title,
35297                 msg : msg,
35298                 buttons: this.OKCANCEL,
35299                 fn: fn,
35300                 minWidth:250,
35301                 scope : scope,
35302                 prompt:true,
35303                 multiline: multiline,
35304                 modal : true
35305             });
35306             return this;
35307         },
35308
35309         /**
35310          * Button config that displays a single OK button
35311          * @type Object
35312          */
35313         OK : {ok:true},
35314         /**
35315          * Button config that displays Yes and No buttons
35316          * @type Object
35317          */
35318         YESNO : {yes:true, no:true},
35319         /**
35320          * Button config that displays OK and Cancel buttons
35321          * @type Object
35322          */
35323         OKCANCEL : {ok:true, cancel:true},
35324         /**
35325          * Button config that displays Yes, No and Cancel buttons
35326          * @type Object
35327          */
35328         YESNOCANCEL : {yes:true, no:true, cancel:true},
35329
35330         /**
35331          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35332          * @type Number
35333          */
35334         defaultTextHeight : 75,
35335         /**
35336          * The maximum width in pixels of the message box (defaults to 600)
35337          * @type Number
35338          */
35339         maxWidth : 600,
35340         /**
35341          * The minimum width in pixels of the message box (defaults to 100)
35342          * @type Number
35343          */
35344         minWidth : 100,
35345         /**
35346          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35347          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35348          * @type Number
35349          */
35350         minProgressWidth : 250,
35351         /**
35352          * An object containing the default button text strings that can be overriden for localized language support.
35353          * Supported properties are: ok, cancel, yes and no.
35354          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35355          * @type Object
35356          */
35357         buttonText : {
35358             ok : "OK",
35359             cancel : "Cancel",
35360             yes : "Yes",
35361             no : "No"
35362         }
35363     };
35364 }();
35365
35366 /**
35367  * Shorthand for {@link Roo.MessageBox}
35368  */
35369 Roo.Msg = Roo.MessageBox;/*
35370  * Based on:
35371  * Ext JS Library 1.1.1
35372  * Copyright(c) 2006-2007, Ext JS, LLC.
35373  *
35374  * Originally Released Under LGPL - original licence link has changed is not relivant.
35375  *
35376  * Fork - LGPL
35377  * <script type="text/javascript">
35378  */
35379 /**
35380  * @class Roo.QuickTips
35381  * Provides attractive and customizable tooltips for any element.
35382  * @static
35383  */
35384 Roo.QuickTips = function(){
35385     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35386     var ce, bd, xy, dd;
35387     var visible = false, disabled = true, inited = false;
35388     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35389     
35390     var onOver = function(e){
35391         if(disabled){
35392             return;
35393         }
35394         var t = e.getTarget();
35395         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35396             return;
35397         }
35398         if(ce && t == ce.el){
35399             clearTimeout(hideProc);
35400             return;
35401         }
35402         if(t && tagEls[t.id]){
35403             tagEls[t.id].el = t;
35404             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35405             return;
35406         }
35407         var ttp, et = Roo.fly(t);
35408         var ns = cfg.namespace;
35409         if(tm.interceptTitles && t.title){
35410             ttp = t.title;
35411             t.qtip = ttp;
35412             t.removeAttribute("title");
35413             e.preventDefault();
35414         }else{
35415             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35416         }
35417         if(ttp){
35418             showProc = show.defer(tm.showDelay, tm, [{
35419                 el: t, 
35420                 text: ttp.replace(/\\n/g,'<br/>'),
35421                 width: et.getAttributeNS(ns, cfg.width),
35422                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35423                 title: et.getAttributeNS(ns, cfg.title),
35424                     cls: et.getAttributeNS(ns, cfg.cls)
35425             }]);
35426         }
35427     };
35428     
35429     var onOut = function(e){
35430         clearTimeout(showProc);
35431         var t = e.getTarget();
35432         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35433             hideProc = setTimeout(hide, tm.hideDelay);
35434         }
35435     };
35436     
35437     var onMove = function(e){
35438         if(disabled){
35439             return;
35440         }
35441         xy = e.getXY();
35442         xy[1] += 18;
35443         if(tm.trackMouse && ce){
35444             el.setXY(xy);
35445         }
35446     };
35447     
35448     var onDown = function(e){
35449         clearTimeout(showProc);
35450         clearTimeout(hideProc);
35451         if(!e.within(el)){
35452             if(tm.hideOnClick){
35453                 hide();
35454                 tm.disable();
35455                 tm.enable.defer(100, tm);
35456             }
35457         }
35458     };
35459     
35460     var getPad = function(){
35461         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35462     };
35463
35464     var show = function(o){
35465         if(disabled){
35466             return;
35467         }
35468         clearTimeout(dismissProc);
35469         ce = o;
35470         if(removeCls){ // in case manually hidden
35471             el.removeClass(removeCls);
35472             removeCls = null;
35473         }
35474         if(ce.cls){
35475             el.addClass(ce.cls);
35476             removeCls = ce.cls;
35477         }
35478         if(ce.title){
35479             tipTitle.update(ce.title);
35480             tipTitle.show();
35481         }else{
35482             tipTitle.update('');
35483             tipTitle.hide();
35484         }
35485         el.dom.style.width  = tm.maxWidth+'px';
35486         //tipBody.dom.style.width = '';
35487         tipBodyText.update(o.text);
35488         var p = getPad(), w = ce.width;
35489         if(!w){
35490             var td = tipBodyText.dom;
35491             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35492             if(aw > tm.maxWidth){
35493                 w = tm.maxWidth;
35494             }else if(aw < tm.minWidth){
35495                 w = tm.minWidth;
35496             }else{
35497                 w = aw;
35498             }
35499         }
35500         //tipBody.setWidth(w);
35501         el.setWidth(parseInt(w, 10) + p);
35502         if(ce.autoHide === false){
35503             close.setDisplayed(true);
35504             if(dd){
35505                 dd.unlock();
35506             }
35507         }else{
35508             close.setDisplayed(false);
35509             if(dd){
35510                 dd.lock();
35511             }
35512         }
35513         if(xy){
35514             el.avoidY = xy[1]-18;
35515             el.setXY(xy);
35516         }
35517         if(tm.animate){
35518             el.setOpacity(.1);
35519             el.setStyle("visibility", "visible");
35520             el.fadeIn({callback: afterShow});
35521         }else{
35522             afterShow();
35523         }
35524     };
35525     
35526     var afterShow = function(){
35527         if(ce){
35528             el.show();
35529             esc.enable();
35530             if(tm.autoDismiss && ce.autoHide !== false){
35531                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35532             }
35533         }
35534     };
35535     
35536     var hide = function(noanim){
35537         clearTimeout(dismissProc);
35538         clearTimeout(hideProc);
35539         ce = null;
35540         if(el.isVisible()){
35541             esc.disable();
35542             if(noanim !== true && tm.animate){
35543                 el.fadeOut({callback: afterHide});
35544             }else{
35545                 afterHide();
35546             } 
35547         }
35548     };
35549     
35550     var afterHide = function(){
35551         el.hide();
35552         if(removeCls){
35553             el.removeClass(removeCls);
35554             removeCls = null;
35555         }
35556     };
35557     
35558     return {
35559         /**
35560         * @cfg {Number} minWidth
35561         * The minimum width of the quick tip (defaults to 40)
35562         */
35563        minWidth : 40,
35564         /**
35565         * @cfg {Number} maxWidth
35566         * The maximum width of the quick tip (defaults to 300)
35567         */
35568        maxWidth : 300,
35569         /**
35570         * @cfg {Boolean} interceptTitles
35571         * True to automatically use the element's DOM title value if available (defaults to false)
35572         */
35573        interceptTitles : false,
35574         /**
35575         * @cfg {Boolean} trackMouse
35576         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35577         */
35578        trackMouse : false,
35579         /**
35580         * @cfg {Boolean} hideOnClick
35581         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35582         */
35583        hideOnClick : true,
35584         /**
35585         * @cfg {Number} showDelay
35586         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35587         */
35588        showDelay : 500,
35589         /**
35590         * @cfg {Number} hideDelay
35591         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35592         */
35593        hideDelay : 200,
35594         /**
35595         * @cfg {Boolean} autoHide
35596         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35597         * Used in conjunction with hideDelay.
35598         */
35599        autoHide : true,
35600         /**
35601         * @cfg {Boolean}
35602         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35603         * (defaults to true).  Used in conjunction with autoDismissDelay.
35604         */
35605        autoDismiss : true,
35606         /**
35607         * @cfg {Number}
35608         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35609         */
35610        autoDismissDelay : 5000,
35611        /**
35612         * @cfg {Boolean} animate
35613         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35614         */
35615        animate : false,
35616
35617        /**
35618         * @cfg {String} title
35619         * Title text to display (defaults to '').  This can be any valid HTML markup.
35620         */
35621         title: '',
35622        /**
35623         * @cfg {String} text
35624         * Body text to display (defaults to '').  This can be any valid HTML markup.
35625         */
35626         text : '',
35627        /**
35628         * @cfg {String} cls
35629         * A CSS class to apply to the base quick tip element (defaults to '').
35630         */
35631         cls : '',
35632        /**
35633         * @cfg {Number} width
35634         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35635         * minWidth or maxWidth.
35636         */
35637         width : null,
35638
35639     /**
35640      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35641      * or display QuickTips in a page.
35642      */
35643        init : function(){
35644           tm = Roo.QuickTips;
35645           cfg = tm.tagConfig;
35646           if(!inited){
35647               if(!Roo.isReady){ // allow calling of init() before onReady
35648                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35649                   return;
35650               }
35651               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35652               el.fxDefaults = {stopFx: true};
35653               // maximum custom styling
35654               //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>');
35655               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>');              
35656               tipTitle = el.child('h3');
35657               tipTitle.enableDisplayMode("block");
35658               tipBody = el.child('div.x-tip-bd');
35659               tipBodyText = el.child('div.x-tip-bd-inner');
35660               //bdLeft = el.child('div.x-tip-bd-left');
35661               //bdRight = el.child('div.x-tip-bd-right');
35662               close = el.child('div.x-tip-close');
35663               close.enableDisplayMode("block");
35664               close.on("click", hide);
35665               var d = Roo.get(document);
35666               d.on("mousedown", onDown);
35667               d.on("mouseover", onOver);
35668               d.on("mouseout", onOut);
35669               d.on("mousemove", onMove);
35670               esc = d.addKeyListener(27, hide);
35671               esc.disable();
35672               if(Roo.dd.DD){
35673                   dd = el.initDD("default", null, {
35674                       onDrag : function(){
35675                           el.sync();  
35676                       }
35677                   });
35678                   dd.setHandleElId(tipTitle.id);
35679                   dd.lock();
35680               }
35681               inited = true;
35682           }
35683           this.enable(); 
35684        },
35685
35686     /**
35687      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35688      * are supported:
35689      * <pre>
35690 Property    Type                   Description
35691 ----------  ---------------------  ------------------------------------------------------------------------
35692 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35693      * </ul>
35694      * @param {Object} config The config object
35695      */
35696        register : function(config){
35697            var cs = config instanceof Array ? config : arguments;
35698            for(var i = 0, len = cs.length; i < len; i++) {
35699                var c = cs[i];
35700                var target = c.target;
35701                if(target){
35702                    if(target instanceof Array){
35703                        for(var j = 0, jlen = target.length; j < jlen; j++){
35704                            tagEls[target[j]] = c;
35705                        }
35706                    }else{
35707                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35708                    }
35709                }
35710            }
35711        },
35712
35713     /**
35714      * Removes this quick tip from its element and destroys it.
35715      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35716      */
35717        unregister : function(el){
35718            delete tagEls[Roo.id(el)];
35719        },
35720
35721     /**
35722      * Enable this quick tip.
35723      */
35724        enable : function(){
35725            if(inited && disabled){
35726                locks.pop();
35727                if(locks.length < 1){
35728                    disabled = false;
35729                }
35730            }
35731        },
35732
35733     /**
35734      * Disable this quick tip.
35735      */
35736        disable : function(){
35737           disabled = true;
35738           clearTimeout(showProc);
35739           clearTimeout(hideProc);
35740           clearTimeout(dismissProc);
35741           if(ce){
35742               hide(true);
35743           }
35744           locks.push(1);
35745        },
35746
35747     /**
35748      * Returns true if the quick tip is enabled, else false.
35749      */
35750        isEnabled : function(){
35751             return !disabled;
35752        },
35753
35754         // private
35755        tagConfig : {
35756            namespace : "roo", // was ext?? this may break..
35757            alt_namespace : "ext",
35758            attribute : "qtip",
35759            width : "width",
35760            target : "target",
35761            title : "qtitle",
35762            hide : "hide",
35763            cls : "qclass"
35764        }
35765    };
35766 }();
35767
35768 // backwards compat
35769 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35770  * Based on:
35771  * Ext JS Library 1.1.1
35772  * Copyright(c) 2006-2007, Ext JS, LLC.
35773  *
35774  * Originally Released Under LGPL - original licence link has changed is not relivant.
35775  *
35776  * Fork - LGPL
35777  * <script type="text/javascript">
35778  */
35779  
35780
35781 /**
35782  * @class Roo.tree.TreePanel
35783  * @extends Roo.data.Tree
35784  * @cfg {Roo.tree.TreeNode} root The root node
35785  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35786  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35787  * @cfg {Boolean} enableDD true to enable drag and drop
35788  * @cfg {Boolean} enableDrag true to enable just drag
35789  * @cfg {Boolean} enableDrop true to enable just drop
35790  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35791  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35792  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35793  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35794  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35795  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35796  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35797  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35798  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35799  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35800  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35801  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35802  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35803  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35804  * @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>
35805  * @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>
35806  * 
35807  * @constructor
35808  * @param {String/HTMLElement/Element} el The container element
35809  * @param {Object} config
35810  */
35811 Roo.tree.TreePanel = function(el, config){
35812     var root = false;
35813     var loader = false;
35814     if (config.root) {
35815         root = config.root;
35816         delete config.root;
35817     }
35818     if (config.loader) {
35819         loader = config.loader;
35820         delete config.loader;
35821     }
35822     
35823     Roo.apply(this, config);
35824     Roo.tree.TreePanel.superclass.constructor.call(this);
35825     this.el = Roo.get(el);
35826     this.el.addClass('x-tree');
35827     //console.log(root);
35828     if (root) {
35829         this.setRootNode( Roo.factory(root, Roo.tree));
35830     }
35831     if (loader) {
35832         this.loader = Roo.factory(loader, Roo.tree);
35833     }
35834    /**
35835     * Read-only. The id of the container element becomes this TreePanel's id.
35836     */
35837     this.id = this.el.id;
35838     this.addEvents({
35839         /**
35840         * @event beforeload
35841         * Fires before a node is loaded, return false to cancel
35842         * @param {Node} node The node being loaded
35843         */
35844         "beforeload" : true,
35845         /**
35846         * @event load
35847         * Fires when a node is loaded
35848         * @param {Node} node The node that was loaded
35849         */
35850         "load" : true,
35851         /**
35852         * @event textchange
35853         * Fires when the text for a node is changed
35854         * @param {Node} node The node
35855         * @param {String} text The new text
35856         * @param {String} oldText The old text
35857         */
35858         "textchange" : true,
35859         /**
35860         * @event beforeexpand
35861         * Fires before a node is expanded, return false to cancel.
35862         * @param {Node} node The node
35863         * @param {Boolean} deep
35864         * @param {Boolean} anim
35865         */
35866         "beforeexpand" : true,
35867         /**
35868         * @event beforecollapse
35869         * Fires before a node is collapsed, return false to cancel.
35870         * @param {Node} node The node
35871         * @param {Boolean} deep
35872         * @param {Boolean} anim
35873         */
35874         "beforecollapse" : true,
35875         /**
35876         * @event expand
35877         * Fires when a node is expanded
35878         * @param {Node} node The node
35879         */
35880         "expand" : true,
35881         /**
35882         * @event disabledchange
35883         * Fires when the disabled status of a node changes
35884         * @param {Node} node The node
35885         * @param {Boolean} disabled
35886         */
35887         "disabledchange" : true,
35888         /**
35889         * @event collapse
35890         * Fires when a node is collapsed
35891         * @param {Node} node The node
35892         */
35893         "collapse" : true,
35894         /**
35895         * @event beforeclick
35896         * Fires before click processing on a node. Return false to cancel the default action.
35897         * @param {Node} node The node
35898         * @param {Roo.EventObject} e The event object
35899         */
35900         "beforeclick":true,
35901         /**
35902         * @event checkchange
35903         * Fires when a node with a checkbox's checked property changes
35904         * @param {Node} this This node
35905         * @param {Boolean} checked
35906         */
35907         "checkchange":true,
35908         /**
35909         * @event click
35910         * Fires when a node is clicked
35911         * @param {Node} node The node
35912         * @param {Roo.EventObject} e The event object
35913         */
35914         "click":true,
35915         /**
35916         * @event dblclick
35917         * Fires when a node is double clicked
35918         * @param {Node} node The node
35919         * @param {Roo.EventObject} e The event object
35920         */
35921         "dblclick":true,
35922         /**
35923         * @event contextmenu
35924         * Fires when a node is right clicked
35925         * @param {Node} node The node
35926         * @param {Roo.EventObject} e The event object
35927         */
35928         "contextmenu":true,
35929         /**
35930         * @event beforechildrenrendered
35931         * Fires right before the child nodes for a node are rendered
35932         * @param {Node} node The node
35933         */
35934         "beforechildrenrendered":true,
35935         /**
35936         * @event startdrag
35937         * Fires when a node starts being dragged
35938         * @param {Roo.tree.TreePanel} this
35939         * @param {Roo.tree.TreeNode} node
35940         * @param {event} e The raw browser event
35941         */ 
35942        "startdrag" : true,
35943        /**
35944         * @event enddrag
35945         * Fires when a drag operation is complete
35946         * @param {Roo.tree.TreePanel} this
35947         * @param {Roo.tree.TreeNode} node
35948         * @param {event} e The raw browser event
35949         */
35950        "enddrag" : true,
35951        /**
35952         * @event dragdrop
35953         * Fires when a dragged node is dropped on a valid DD target
35954         * @param {Roo.tree.TreePanel} this
35955         * @param {Roo.tree.TreeNode} node
35956         * @param {DD} dd The dd it was dropped on
35957         * @param {event} e The raw browser event
35958         */
35959        "dragdrop" : true,
35960        /**
35961         * @event beforenodedrop
35962         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35963         * passed to handlers has the following properties:<br />
35964         * <ul style="padding:5px;padding-left:16px;">
35965         * <li>tree - The TreePanel</li>
35966         * <li>target - The node being targeted for the drop</li>
35967         * <li>data - The drag data from the drag source</li>
35968         * <li>point - The point of the drop - append, above or below</li>
35969         * <li>source - The drag source</li>
35970         * <li>rawEvent - Raw mouse event</li>
35971         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35972         * to be inserted by setting them on this object.</li>
35973         * <li>cancel - Set this to true to cancel the drop.</li>
35974         * </ul>
35975         * @param {Object} dropEvent
35976         */
35977        "beforenodedrop" : true,
35978        /**
35979         * @event nodedrop
35980         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35981         * passed to handlers has the following properties:<br />
35982         * <ul style="padding:5px;padding-left:16px;">
35983         * <li>tree - The TreePanel</li>
35984         * <li>target - The node being targeted for the drop</li>
35985         * <li>data - The drag data from the drag source</li>
35986         * <li>point - The point of the drop - append, above or below</li>
35987         * <li>source - The drag source</li>
35988         * <li>rawEvent - Raw mouse event</li>
35989         * <li>dropNode - Dropped node(s).</li>
35990         * </ul>
35991         * @param {Object} dropEvent
35992         */
35993        "nodedrop" : true,
35994         /**
35995         * @event nodedragover
35996         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35997         * passed to handlers has the following properties:<br />
35998         * <ul style="padding:5px;padding-left:16px;">
35999         * <li>tree - The TreePanel</li>
36000         * <li>target - The node being targeted for the drop</li>
36001         * <li>data - The drag data from the drag source</li>
36002         * <li>point - The point of the drop - append, above or below</li>
36003         * <li>source - The drag source</li>
36004         * <li>rawEvent - Raw mouse event</li>
36005         * <li>dropNode - Drop node(s) provided by the source.</li>
36006         * <li>cancel - Set this to true to signal drop not allowed.</li>
36007         * </ul>
36008         * @param {Object} dragOverEvent
36009         */
36010        "nodedragover" : true,
36011        /**
36012         * @event appendnode
36013         * Fires when append node to the tree
36014         * @param {Roo.tree.TreePanel} this
36015         * @param {Roo.tree.TreeNode} node
36016         * @param {Number} index The index of the newly appended node
36017         */
36018        "appendnode" : true
36019         
36020     });
36021     if(this.singleExpand){
36022        this.on("beforeexpand", this.restrictExpand, this);
36023     }
36024     if (this.editor) {
36025         this.editor.tree = this;
36026         this.editor = Roo.factory(this.editor, Roo.tree);
36027     }
36028     
36029     if (this.selModel) {
36030         this.selModel = Roo.factory(this.selModel, Roo.tree);
36031     }
36032    
36033 };
36034 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36035     rootVisible : true,
36036     animate: Roo.enableFx,
36037     lines : true,
36038     enableDD : false,
36039     hlDrop : Roo.enableFx,
36040   
36041     renderer: false,
36042     
36043     rendererTip: false,
36044     // private
36045     restrictExpand : function(node){
36046         var p = node.parentNode;
36047         if(p){
36048             if(p.expandedChild && p.expandedChild.parentNode == p){
36049                 p.expandedChild.collapse();
36050             }
36051             p.expandedChild = node;
36052         }
36053     },
36054
36055     // private override
36056     setRootNode : function(node){
36057         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36058         if(!this.rootVisible){
36059             node.ui = new Roo.tree.RootTreeNodeUI(node);
36060         }
36061         return node;
36062     },
36063
36064     /**
36065      * Returns the container element for this TreePanel
36066      */
36067     getEl : function(){
36068         return this.el;
36069     },
36070
36071     /**
36072      * Returns the default TreeLoader for this TreePanel
36073      */
36074     getLoader : function(){
36075         return this.loader;
36076     },
36077
36078     /**
36079      * Expand all nodes
36080      */
36081     expandAll : function(){
36082         this.root.expand(true);
36083     },
36084
36085     /**
36086      * Collapse all nodes
36087      */
36088     collapseAll : function(){
36089         this.root.collapse(true);
36090     },
36091
36092     /**
36093      * Returns the selection model used by this TreePanel
36094      */
36095     getSelectionModel : function(){
36096         if(!this.selModel){
36097             this.selModel = new Roo.tree.DefaultSelectionModel();
36098         }
36099         return this.selModel;
36100     },
36101
36102     /**
36103      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36104      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36105      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36106      * @return {Array}
36107      */
36108     getChecked : function(a, startNode){
36109         startNode = startNode || this.root;
36110         var r = [];
36111         var f = function(){
36112             if(this.attributes.checked){
36113                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36114             }
36115         }
36116         startNode.cascade(f);
36117         return r;
36118     },
36119
36120     /**
36121      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36122      * @param {String} path
36123      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36124      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36125      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36126      */
36127     expandPath : function(path, attr, callback){
36128         attr = attr || "id";
36129         var keys = path.split(this.pathSeparator);
36130         var curNode = this.root;
36131         if(curNode.attributes[attr] != keys[1]){ // invalid root
36132             if(callback){
36133                 callback(false, null);
36134             }
36135             return;
36136         }
36137         var index = 1;
36138         var f = function(){
36139             if(++index == keys.length){
36140                 if(callback){
36141                     callback(true, curNode);
36142                 }
36143                 return;
36144             }
36145             var c = curNode.findChild(attr, keys[index]);
36146             if(!c){
36147                 if(callback){
36148                     callback(false, curNode);
36149                 }
36150                 return;
36151             }
36152             curNode = c;
36153             c.expand(false, false, f);
36154         };
36155         curNode.expand(false, false, f);
36156     },
36157
36158     /**
36159      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36160      * @param {String} path
36161      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36162      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36163      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36164      */
36165     selectPath : function(path, attr, callback){
36166         attr = attr || "id";
36167         var keys = path.split(this.pathSeparator);
36168         var v = keys.pop();
36169         if(keys.length > 0){
36170             var f = function(success, node){
36171                 if(success && node){
36172                     var n = node.findChild(attr, v);
36173                     if(n){
36174                         n.select();
36175                         if(callback){
36176                             callback(true, n);
36177                         }
36178                     }else if(callback){
36179                         callback(false, n);
36180                     }
36181                 }else{
36182                     if(callback){
36183                         callback(false, n);
36184                     }
36185                 }
36186             };
36187             this.expandPath(keys.join(this.pathSeparator), attr, f);
36188         }else{
36189             this.root.select();
36190             if(callback){
36191                 callback(true, this.root);
36192             }
36193         }
36194     },
36195
36196     getTreeEl : function(){
36197         return this.el;
36198     },
36199
36200     /**
36201      * Trigger rendering of this TreePanel
36202      */
36203     render : function(){
36204         if (this.innerCt) {
36205             return this; // stop it rendering more than once!!
36206         }
36207         
36208         this.innerCt = this.el.createChild({tag:"ul",
36209                cls:"x-tree-root-ct " +
36210                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36211
36212         if(this.containerScroll){
36213             Roo.dd.ScrollManager.register(this.el);
36214         }
36215         if((this.enableDD || this.enableDrop) && !this.dropZone){
36216            /**
36217             * The dropZone used by this tree if drop is enabled
36218             * @type Roo.tree.TreeDropZone
36219             */
36220              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36221                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36222            });
36223         }
36224         if((this.enableDD || this.enableDrag) && !this.dragZone){
36225            /**
36226             * The dragZone used by this tree if drag is enabled
36227             * @type Roo.tree.TreeDragZone
36228             */
36229             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36230                ddGroup: this.ddGroup || "TreeDD",
36231                scroll: this.ddScroll
36232            });
36233         }
36234         this.getSelectionModel().init(this);
36235         if (!this.root) {
36236             Roo.log("ROOT not set in tree");
36237             return this;
36238         }
36239         this.root.render();
36240         if(!this.rootVisible){
36241             this.root.renderChildren();
36242         }
36243         return this;
36244     }
36245 });/*
36246  * Based on:
36247  * Ext JS Library 1.1.1
36248  * Copyright(c) 2006-2007, Ext JS, LLC.
36249  *
36250  * Originally Released Under LGPL - original licence link has changed is not relivant.
36251  *
36252  * Fork - LGPL
36253  * <script type="text/javascript">
36254  */
36255  
36256
36257 /**
36258  * @class Roo.tree.DefaultSelectionModel
36259  * @extends Roo.util.Observable
36260  * The default single selection for a TreePanel.
36261  * @param {Object} cfg Configuration
36262  */
36263 Roo.tree.DefaultSelectionModel = function(cfg){
36264    this.selNode = null;
36265    
36266    
36267    
36268    this.addEvents({
36269        /**
36270         * @event selectionchange
36271         * Fires when the selected node changes
36272         * @param {DefaultSelectionModel} this
36273         * @param {TreeNode} node the new selection
36274         */
36275        "selectionchange" : true,
36276
36277        /**
36278         * @event beforeselect
36279         * Fires before the selected node changes, return false to cancel the change
36280         * @param {DefaultSelectionModel} this
36281         * @param {TreeNode} node the new selection
36282         * @param {TreeNode} node the old selection
36283         */
36284        "beforeselect" : true
36285    });
36286    
36287     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36288 };
36289
36290 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36291     init : function(tree){
36292         this.tree = tree;
36293         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36294         tree.on("click", this.onNodeClick, this);
36295     },
36296     
36297     onNodeClick : function(node, e){
36298         if (e.ctrlKey && this.selNode == node)  {
36299             this.unselect(node);
36300             return;
36301         }
36302         this.select(node);
36303     },
36304     
36305     /**
36306      * Select a node.
36307      * @param {TreeNode} node The node to select
36308      * @return {TreeNode} The selected node
36309      */
36310     select : function(node){
36311         var last = this.selNode;
36312         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36313             if(last){
36314                 last.ui.onSelectedChange(false);
36315             }
36316             this.selNode = node;
36317             node.ui.onSelectedChange(true);
36318             this.fireEvent("selectionchange", this, node, last);
36319         }
36320         return node;
36321     },
36322     
36323     /**
36324      * Deselect a node.
36325      * @param {TreeNode} node The node to unselect
36326      */
36327     unselect : function(node){
36328         if(this.selNode == node){
36329             this.clearSelections();
36330         }    
36331     },
36332     
36333     /**
36334      * Clear all selections
36335      */
36336     clearSelections : function(){
36337         var n = this.selNode;
36338         if(n){
36339             n.ui.onSelectedChange(false);
36340             this.selNode = null;
36341             this.fireEvent("selectionchange", this, null);
36342         }
36343         return n;
36344     },
36345     
36346     /**
36347      * Get the selected node
36348      * @return {TreeNode} The selected node
36349      */
36350     getSelectedNode : function(){
36351         return this.selNode;    
36352     },
36353     
36354     /**
36355      * Returns true if the node is selected
36356      * @param {TreeNode} node The node to check
36357      * @return {Boolean}
36358      */
36359     isSelected : function(node){
36360         return this.selNode == node;  
36361     },
36362
36363     /**
36364      * Selects the node above the selected node in the tree, intelligently walking the nodes
36365      * @return TreeNode The new selection
36366      */
36367     selectPrevious : function(){
36368         var s = this.selNode || this.lastSelNode;
36369         if(!s){
36370             return null;
36371         }
36372         var ps = s.previousSibling;
36373         if(ps){
36374             if(!ps.isExpanded() || ps.childNodes.length < 1){
36375                 return this.select(ps);
36376             } else{
36377                 var lc = ps.lastChild;
36378                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36379                     lc = lc.lastChild;
36380                 }
36381                 return this.select(lc);
36382             }
36383         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36384             return this.select(s.parentNode);
36385         }
36386         return null;
36387     },
36388
36389     /**
36390      * Selects the node above the selected node in the tree, intelligently walking the nodes
36391      * @return TreeNode The new selection
36392      */
36393     selectNext : function(){
36394         var s = this.selNode || this.lastSelNode;
36395         if(!s){
36396             return null;
36397         }
36398         if(s.firstChild && s.isExpanded()){
36399              return this.select(s.firstChild);
36400          }else if(s.nextSibling){
36401              return this.select(s.nextSibling);
36402          }else if(s.parentNode){
36403             var newS = null;
36404             s.parentNode.bubble(function(){
36405                 if(this.nextSibling){
36406                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36407                     return false;
36408                 }
36409             });
36410             return newS;
36411          }
36412         return null;
36413     },
36414
36415     onKeyDown : function(e){
36416         var s = this.selNode || this.lastSelNode;
36417         // undesirable, but required
36418         var sm = this;
36419         if(!s){
36420             return;
36421         }
36422         var k = e.getKey();
36423         switch(k){
36424              case e.DOWN:
36425                  e.stopEvent();
36426                  this.selectNext();
36427              break;
36428              case e.UP:
36429                  e.stopEvent();
36430                  this.selectPrevious();
36431              break;
36432              case e.RIGHT:
36433                  e.preventDefault();
36434                  if(s.hasChildNodes()){
36435                      if(!s.isExpanded()){
36436                          s.expand();
36437                      }else if(s.firstChild){
36438                          this.select(s.firstChild, e);
36439                      }
36440                  }
36441              break;
36442              case e.LEFT:
36443                  e.preventDefault();
36444                  if(s.hasChildNodes() && s.isExpanded()){
36445                      s.collapse();
36446                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36447                      this.select(s.parentNode, e);
36448                  }
36449              break;
36450         };
36451     }
36452 });
36453
36454 /**
36455  * @class Roo.tree.MultiSelectionModel
36456  * @extends Roo.util.Observable
36457  * Multi selection for a TreePanel.
36458  * @param {Object} cfg Configuration
36459  */
36460 Roo.tree.MultiSelectionModel = function(){
36461    this.selNodes = [];
36462    this.selMap = {};
36463    this.addEvents({
36464        /**
36465         * @event selectionchange
36466         * Fires when the selected nodes change
36467         * @param {MultiSelectionModel} this
36468         * @param {Array} nodes Array of the selected nodes
36469         */
36470        "selectionchange" : true
36471    });
36472    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36473    
36474 };
36475
36476 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36477     init : function(tree){
36478         this.tree = tree;
36479         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36480         tree.on("click", this.onNodeClick, this);
36481     },
36482     
36483     onNodeClick : function(node, e){
36484         this.select(node, e, e.ctrlKey);
36485     },
36486     
36487     /**
36488      * Select a node.
36489      * @param {TreeNode} node The node to select
36490      * @param {EventObject} e (optional) An event associated with the selection
36491      * @param {Boolean} keepExisting True to retain existing selections
36492      * @return {TreeNode} The selected node
36493      */
36494     select : function(node, e, keepExisting){
36495         if(keepExisting !== true){
36496             this.clearSelections(true);
36497         }
36498         if(this.isSelected(node)){
36499             this.lastSelNode = node;
36500             return node;
36501         }
36502         this.selNodes.push(node);
36503         this.selMap[node.id] = node;
36504         this.lastSelNode = node;
36505         node.ui.onSelectedChange(true);
36506         this.fireEvent("selectionchange", this, this.selNodes);
36507         return node;
36508     },
36509     
36510     /**
36511      * Deselect a node.
36512      * @param {TreeNode} node The node to unselect
36513      */
36514     unselect : function(node){
36515         if(this.selMap[node.id]){
36516             node.ui.onSelectedChange(false);
36517             var sn = this.selNodes;
36518             var index = -1;
36519             if(sn.indexOf){
36520                 index = sn.indexOf(node);
36521             }else{
36522                 for(var i = 0, len = sn.length; i < len; i++){
36523                     if(sn[i] == node){
36524                         index = i;
36525                         break;
36526                     }
36527                 }
36528             }
36529             if(index != -1){
36530                 this.selNodes.splice(index, 1);
36531             }
36532             delete this.selMap[node.id];
36533             this.fireEvent("selectionchange", this, this.selNodes);
36534         }
36535     },
36536     
36537     /**
36538      * Clear all selections
36539      */
36540     clearSelections : function(suppressEvent){
36541         var sn = this.selNodes;
36542         if(sn.length > 0){
36543             for(var i = 0, len = sn.length; i < len; i++){
36544                 sn[i].ui.onSelectedChange(false);
36545             }
36546             this.selNodes = [];
36547             this.selMap = {};
36548             if(suppressEvent !== true){
36549                 this.fireEvent("selectionchange", this, this.selNodes);
36550             }
36551         }
36552     },
36553     
36554     /**
36555      * Returns true if the node is selected
36556      * @param {TreeNode} node The node to check
36557      * @return {Boolean}
36558      */
36559     isSelected : function(node){
36560         return this.selMap[node.id] ? true : false;  
36561     },
36562     
36563     /**
36564      * Returns an array of the selected nodes
36565      * @return {Array}
36566      */
36567     getSelectedNodes : function(){
36568         return this.selNodes;    
36569     },
36570
36571     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36572
36573     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36574
36575     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36576 });/*
36577  * Based on:
36578  * Ext JS Library 1.1.1
36579  * Copyright(c) 2006-2007, Ext JS, LLC.
36580  *
36581  * Originally Released Under LGPL - original licence link has changed is not relivant.
36582  *
36583  * Fork - LGPL
36584  * <script type="text/javascript">
36585  */
36586  
36587 /**
36588  * @class Roo.tree.TreeNode
36589  * @extends Roo.data.Node
36590  * @cfg {String} text The text for this node
36591  * @cfg {Boolean} expanded true to start the node expanded
36592  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36593  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36594  * @cfg {Boolean} disabled true to start the node disabled
36595  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36596  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36597  * @cfg {String} cls A css class to be added to the node
36598  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36599  * @cfg {String} href URL of the link used for the node (defaults to #)
36600  * @cfg {String} hrefTarget target frame for the link
36601  * @cfg {String} qtip An Ext QuickTip for the node
36602  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36603  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36604  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36605  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36606  * (defaults to undefined with no checkbox rendered)
36607  * @constructor
36608  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36609  */
36610 Roo.tree.TreeNode = function(attributes){
36611     attributes = attributes || {};
36612     if(typeof attributes == "string"){
36613         attributes = {text: attributes};
36614     }
36615     this.childrenRendered = false;
36616     this.rendered = false;
36617     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36618     this.expanded = attributes.expanded === true;
36619     this.isTarget = attributes.isTarget !== false;
36620     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36621     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36622
36623     /**
36624      * Read-only. The text for this node. To change it use setText().
36625      * @type String
36626      */
36627     this.text = attributes.text;
36628     /**
36629      * True if this node is disabled.
36630      * @type Boolean
36631      */
36632     this.disabled = attributes.disabled === true;
36633
36634     this.addEvents({
36635         /**
36636         * @event textchange
36637         * Fires when the text for this node is changed
36638         * @param {Node} this This node
36639         * @param {String} text The new text
36640         * @param {String} oldText The old text
36641         */
36642         "textchange" : true,
36643         /**
36644         * @event beforeexpand
36645         * Fires before this node is expanded, return false to cancel.
36646         * @param {Node} this This node
36647         * @param {Boolean} deep
36648         * @param {Boolean} anim
36649         */
36650         "beforeexpand" : true,
36651         /**
36652         * @event beforecollapse
36653         * Fires before this node is collapsed, return false to cancel.
36654         * @param {Node} this This node
36655         * @param {Boolean} deep
36656         * @param {Boolean} anim
36657         */
36658         "beforecollapse" : true,
36659         /**
36660         * @event expand
36661         * Fires when this node is expanded
36662         * @param {Node} this This node
36663         */
36664         "expand" : true,
36665         /**
36666         * @event disabledchange
36667         * Fires when the disabled status of this node changes
36668         * @param {Node} this This node
36669         * @param {Boolean} disabled
36670         */
36671         "disabledchange" : true,
36672         /**
36673         * @event collapse
36674         * Fires when this node is collapsed
36675         * @param {Node} this This node
36676         */
36677         "collapse" : true,
36678         /**
36679         * @event beforeclick
36680         * Fires before click processing. Return false to cancel the default action.
36681         * @param {Node} this This node
36682         * @param {Roo.EventObject} e The event object
36683         */
36684         "beforeclick":true,
36685         /**
36686         * @event checkchange
36687         * Fires when a node with a checkbox's checked property changes
36688         * @param {Node} this This node
36689         * @param {Boolean} checked
36690         */
36691         "checkchange":true,
36692         /**
36693         * @event click
36694         * Fires when this node is clicked
36695         * @param {Node} this This node
36696         * @param {Roo.EventObject} e The event object
36697         */
36698         "click":true,
36699         /**
36700         * @event dblclick
36701         * Fires when this node is double clicked
36702         * @param {Node} this This node
36703         * @param {Roo.EventObject} e The event object
36704         */
36705         "dblclick":true,
36706         /**
36707         * @event contextmenu
36708         * Fires when this node is right clicked
36709         * @param {Node} this This node
36710         * @param {Roo.EventObject} e The event object
36711         */
36712         "contextmenu":true,
36713         /**
36714         * @event beforechildrenrendered
36715         * Fires right before the child nodes for this node are rendered
36716         * @param {Node} this This node
36717         */
36718         "beforechildrenrendered":true
36719     });
36720
36721     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36722
36723     /**
36724      * Read-only. The UI for this node
36725      * @type TreeNodeUI
36726      */
36727     this.ui = new uiClass(this);
36728     
36729     // finally support items[]
36730     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36731         return;
36732     }
36733     
36734     
36735     Roo.each(this.attributes.items, function(c) {
36736         this.appendChild(Roo.factory(c,Roo.Tree));
36737     }, this);
36738     delete this.attributes.items;
36739     
36740     
36741     
36742 };
36743 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36744     preventHScroll: true,
36745     /**
36746      * Returns true if this node is expanded
36747      * @return {Boolean}
36748      */
36749     isExpanded : function(){
36750         return this.expanded;
36751     },
36752
36753     /**
36754      * Returns the UI object for this node
36755      * @return {TreeNodeUI}
36756      */
36757     getUI : function(){
36758         return this.ui;
36759     },
36760
36761     // private override
36762     setFirstChild : function(node){
36763         var of = this.firstChild;
36764         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36765         if(this.childrenRendered && of && node != of){
36766             of.renderIndent(true, true);
36767         }
36768         if(this.rendered){
36769             this.renderIndent(true, true);
36770         }
36771     },
36772
36773     // private override
36774     setLastChild : function(node){
36775         var ol = this.lastChild;
36776         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36777         if(this.childrenRendered && ol && node != ol){
36778             ol.renderIndent(true, true);
36779         }
36780         if(this.rendered){
36781             this.renderIndent(true, true);
36782         }
36783     },
36784
36785     // these methods are overridden to provide lazy rendering support
36786     // private override
36787     appendChild : function()
36788     {
36789         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36790         if(node && this.childrenRendered){
36791             node.render();
36792         }
36793         this.ui.updateExpandIcon();
36794         return node;
36795     },
36796
36797     // private override
36798     removeChild : function(node){
36799         this.ownerTree.getSelectionModel().unselect(node);
36800         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36801         // if it's been rendered remove dom node
36802         if(this.childrenRendered){
36803             node.ui.remove();
36804         }
36805         if(this.childNodes.length < 1){
36806             this.collapse(false, false);
36807         }else{
36808             this.ui.updateExpandIcon();
36809         }
36810         if(!this.firstChild) {
36811             this.childrenRendered = false;
36812         }
36813         return node;
36814     },
36815
36816     // private override
36817     insertBefore : function(node, refNode){
36818         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36819         if(newNode && refNode && this.childrenRendered){
36820             node.render();
36821         }
36822         this.ui.updateExpandIcon();
36823         return newNode;
36824     },
36825
36826     /**
36827      * Sets the text for this node
36828      * @param {String} text
36829      */
36830     setText : function(text){
36831         var oldText = this.text;
36832         this.text = text;
36833         this.attributes.text = text;
36834         if(this.rendered){ // event without subscribing
36835             this.ui.onTextChange(this, text, oldText);
36836         }
36837         this.fireEvent("textchange", this, text, oldText);
36838     },
36839
36840     /**
36841      * Triggers selection of this node
36842      */
36843     select : function(){
36844         this.getOwnerTree().getSelectionModel().select(this);
36845     },
36846
36847     /**
36848      * Triggers deselection of this node
36849      */
36850     unselect : function(){
36851         this.getOwnerTree().getSelectionModel().unselect(this);
36852     },
36853
36854     /**
36855      * Returns true if this node is selected
36856      * @return {Boolean}
36857      */
36858     isSelected : function(){
36859         return this.getOwnerTree().getSelectionModel().isSelected(this);
36860     },
36861
36862     /**
36863      * Expand this node.
36864      * @param {Boolean} deep (optional) True to expand all children as well
36865      * @param {Boolean} anim (optional) false to cancel the default animation
36866      * @param {Function} callback (optional) A callback to be called when
36867      * expanding this node completes (does not wait for deep expand to complete).
36868      * Called with 1 parameter, this node.
36869      */
36870     expand : function(deep, anim, callback){
36871         if(!this.expanded){
36872             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36873                 return;
36874             }
36875             if(!this.childrenRendered){
36876                 this.renderChildren();
36877             }
36878             this.expanded = true;
36879             
36880             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36881                 this.ui.animExpand(function(){
36882                     this.fireEvent("expand", this);
36883                     if(typeof callback == "function"){
36884                         callback(this);
36885                     }
36886                     if(deep === true){
36887                         this.expandChildNodes(true);
36888                     }
36889                 }.createDelegate(this));
36890                 return;
36891             }else{
36892                 this.ui.expand();
36893                 this.fireEvent("expand", this);
36894                 if(typeof callback == "function"){
36895                     callback(this);
36896                 }
36897             }
36898         }else{
36899            if(typeof callback == "function"){
36900                callback(this);
36901            }
36902         }
36903         if(deep === true){
36904             this.expandChildNodes(true);
36905         }
36906     },
36907
36908     isHiddenRoot : function(){
36909         return this.isRoot && !this.getOwnerTree().rootVisible;
36910     },
36911
36912     /**
36913      * Collapse this node.
36914      * @param {Boolean} deep (optional) True to collapse all children as well
36915      * @param {Boolean} anim (optional) false to cancel the default animation
36916      */
36917     collapse : function(deep, anim){
36918         if(this.expanded && !this.isHiddenRoot()){
36919             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36920                 return;
36921             }
36922             this.expanded = false;
36923             if((this.getOwnerTree().animate && anim !== false) || anim){
36924                 this.ui.animCollapse(function(){
36925                     this.fireEvent("collapse", this);
36926                     if(deep === true){
36927                         this.collapseChildNodes(true);
36928                     }
36929                 }.createDelegate(this));
36930                 return;
36931             }else{
36932                 this.ui.collapse();
36933                 this.fireEvent("collapse", this);
36934             }
36935         }
36936         if(deep === true){
36937             var cs = this.childNodes;
36938             for(var i = 0, len = cs.length; i < len; i++) {
36939                 cs[i].collapse(true, false);
36940             }
36941         }
36942     },
36943
36944     // private
36945     delayedExpand : function(delay){
36946         if(!this.expandProcId){
36947             this.expandProcId = this.expand.defer(delay, this);
36948         }
36949     },
36950
36951     // private
36952     cancelExpand : function(){
36953         if(this.expandProcId){
36954             clearTimeout(this.expandProcId);
36955         }
36956         this.expandProcId = false;
36957     },
36958
36959     /**
36960      * Toggles expanded/collapsed state of the node
36961      */
36962     toggle : function(){
36963         if(this.expanded){
36964             this.collapse();
36965         }else{
36966             this.expand();
36967         }
36968     },
36969
36970     /**
36971      * Ensures all parent nodes are expanded
36972      */
36973     ensureVisible : function(callback){
36974         var tree = this.getOwnerTree();
36975         tree.expandPath(this.parentNode.getPath(), false, function(){
36976             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36977             Roo.callback(callback);
36978         }.createDelegate(this));
36979     },
36980
36981     /**
36982      * Expand all child nodes
36983      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36984      */
36985     expandChildNodes : function(deep){
36986         var cs = this.childNodes;
36987         for(var i = 0, len = cs.length; i < len; i++) {
36988                 cs[i].expand(deep);
36989         }
36990     },
36991
36992     /**
36993      * Collapse all child nodes
36994      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36995      */
36996     collapseChildNodes : function(deep){
36997         var cs = this.childNodes;
36998         for(var i = 0, len = cs.length; i < len; i++) {
36999                 cs[i].collapse(deep);
37000         }
37001     },
37002
37003     /**
37004      * Disables this node
37005      */
37006     disable : function(){
37007         this.disabled = true;
37008         this.unselect();
37009         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37010             this.ui.onDisableChange(this, true);
37011         }
37012         this.fireEvent("disabledchange", this, true);
37013     },
37014
37015     /**
37016      * Enables this node
37017      */
37018     enable : function(){
37019         this.disabled = false;
37020         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37021             this.ui.onDisableChange(this, false);
37022         }
37023         this.fireEvent("disabledchange", this, false);
37024     },
37025
37026     // private
37027     renderChildren : function(suppressEvent){
37028         if(suppressEvent !== false){
37029             this.fireEvent("beforechildrenrendered", this);
37030         }
37031         var cs = this.childNodes;
37032         for(var i = 0, len = cs.length; i < len; i++){
37033             cs[i].render(true);
37034         }
37035         this.childrenRendered = true;
37036     },
37037
37038     // private
37039     sort : function(fn, scope){
37040         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37041         if(this.childrenRendered){
37042             var cs = this.childNodes;
37043             for(var i = 0, len = cs.length; i < len; i++){
37044                 cs[i].render(true);
37045             }
37046         }
37047     },
37048
37049     // private
37050     render : function(bulkRender){
37051         this.ui.render(bulkRender);
37052         if(!this.rendered){
37053             this.rendered = true;
37054             if(this.expanded){
37055                 this.expanded = false;
37056                 this.expand(false, false);
37057             }
37058         }
37059     },
37060
37061     // private
37062     renderIndent : function(deep, refresh){
37063         if(refresh){
37064             this.ui.childIndent = null;
37065         }
37066         this.ui.renderIndent();
37067         if(deep === true && this.childrenRendered){
37068             var cs = this.childNodes;
37069             for(var i = 0, len = cs.length; i < len; i++){
37070                 cs[i].renderIndent(true, refresh);
37071             }
37072         }
37073     }
37074 });/*
37075  * Based on:
37076  * Ext JS Library 1.1.1
37077  * Copyright(c) 2006-2007, Ext JS, LLC.
37078  *
37079  * Originally Released Under LGPL - original licence link has changed is not relivant.
37080  *
37081  * Fork - LGPL
37082  * <script type="text/javascript">
37083  */
37084  
37085 /**
37086  * @class Roo.tree.AsyncTreeNode
37087  * @extends Roo.tree.TreeNode
37088  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37089  * @constructor
37090  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
37091  */
37092  Roo.tree.AsyncTreeNode = function(config){
37093     this.loaded = false;
37094     this.loading = false;
37095     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37096     /**
37097     * @event beforeload
37098     * Fires before this node is loaded, return false to cancel
37099     * @param {Node} this This node
37100     */
37101     this.addEvents({'beforeload':true, 'load': true});
37102     /**
37103     * @event load
37104     * Fires when this node is loaded
37105     * @param {Node} this This node
37106     */
37107     /**
37108      * The loader used by this node (defaults to using the tree's defined loader)
37109      * @type TreeLoader
37110      * @property loader
37111      */
37112 };
37113 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37114     expand : function(deep, anim, callback){
37115         if(this.loading){ // if an async load is already running, waiting til it's done
37116             var timer;
37117             var f = function(){
37118                 if(!this.loading){ // done loading
37119                     clearInterval(timer);
37120                     this.expand(deep, anim, callback);
37121                 }
37122             }.createDelegate(this);
37123             timer = setInterval(f, 200);
37124             return;
37125         }
37126         if(!this.loaded){
37127             if(this.fireEvent("beforeload", this) === false){
37128                 return;
37129             }
37130             this.loading = true;
37131             this.ui.beforeLoad(this);
37132             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37133             if(loader){
37134                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37135                 return;
37136             }
37137         }
37138         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37139     },
37140     
37141     /**
37142      * Returns true if this node is currently loading
37143      * @return {Boolean}
37144      */
37145     isLoading : function(){
37146         return this.loading;  
37147     },
37148     
37149     loadComplete : function(deep, anim, callback){
37150         this.loading = false;
37151         this.loaded = true;
37152         this.ui.afterLoad(this);
37153         this.fireEvent("load", this);
37154         this.expand(deep, anim, callback);
37155     },
37156     
37157     /**
37158      * Returns true if this node has been loaded
37159      * @return {Boolean}
37160      */
37161     isLoaded : function(){
37162         return this.loaded;
37163     },
37164     
37165     hasChildNodes : function(){
37166         if(!this.isLeaf() && !this.loaded){
37167             return true;
37168         }else{
37169             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37170         }
37171     },
37172
37173     /**
37174      * Trigger a reload for this node
37175      * @param {Function} callback
37176      */
37177     reload : function(callback){
37178         this.collapse(false, false);
37179         while(this.firstChild){
37180             this.removeChild(this.firstChild);
37181         }
37182         this.childrenRendered = false;
37183         this.loaded = false;
37184         if(this.isHiddenRoot()){
37185             this.expanded = false;
37186         }
37187         this.expand(false, false, callback);
37188     }
37189 });/*
37190  * Based on:
37191  * Ext JS Library 1.1.1
37192  * Copyright(c) 2006-2007, Ext JS, LLC.
37193  *
37194  * Originally Released Under LGPL - original licence link has changed is not relivant.
37195  *
37196  * Fork - LGPL
37197  * <script type="text/javascript">
37198  */
37199  
37200 /**
37201  * @class Roo.tree.TreeNodeUI
37202  * @constructor
37203  * @param {Object} node The node to render
37204  * The TreeNode UI implementation is separate from the
37205  * tree implementation. Unless you are customizing the tree UI,
37206  * you should never have to use this directly.
37207  */
37208 Roo.tree.TreeNodeUI = function(node){
37209     this.node = node;
37210     this.rendered = false;
37211     this.animating = false;
37212     this.emptyIcon = Roo.BLANK_IMAGE_URL;
37213 };
37214
37215 Roo.tree.TreeNodeUI.prototype = {
37216     removeChild : function(node){
37217         if(this.rendered){
37218             this.ctNode.removeChild(node.ui.getEl());
37219         }
37220     },
37221
37222     beforeLoad : function(){
37223          this.addClass("x-tree-node-loading");
37224     },
37225
37226     afterLoad : function(){
37227          this.removeClass("x-tree-node-loading");
37228     },
37229
37230     onTextChange : function(node, text, oldText){
37231         if(this.rendered){
37232             this.textNode.innerHTML = text;
37233         }
37234     },
37235
37236     onDisableChange : function(node, state){
37237         this.disabled = state;
37238         if(state){
37239             this.addClass("x-tree-node-disabled");
37240         }else{
37241             this.removeClass("x-tree-node-disabled");
37242         }
37243     },
37244
37245     onSelectedChange : function(state){
37246         if(state){
37247             this.focus();
37248             this.addClass("x-tree-selected");
37249         }else{
37250             //this.blur();
37251             this.removeClass("x-tree-selected");
37252         }
37253     },
37254
37255     onMove : function(tree, node, oldParent, newParent, index, refNode){
37256         this.childIndent = null;
37257         if(this.rendered){
37258             var targetNode = newParent.ui.getContainer();
37259             if(!targetNode){//target not rendered
37260                 this.holder = document.createElement("div");
37261                 this.holder.appendChild(this.wrap);
37262                 return;
37263             }
37264             var insertBefore = refNode ? refNode.ui.getEl() : null;
37265             if(insertBefore){
37266                 targetNode.insertBefore(this.wrap, insertBefore);
37267             }else{
37268                 targetNode.appendChild(this.wrap);
37269             }
37270             this.node.renderIndent(true);
37271         }
37272     },
37273
37274     addClass : function(cls){
37275         if(this.elNode){
37276             Roo.fly(this.elNode).addClass(cls);
37277         }
37278     },
37279
37280     removeClass : function(cls){
37281         if(this.elNode){
37282             Roo.fly(this.elNode).removeClass(cls);
37283         }
37284     },
37285
37286     remove : function(){
37287         if(this.rendered){
37288             this.holder = document.createElement("div");
37289             this.holder.appendChild(this.wrap);
37290         }
37291     },
37292
37293     fireEvent : function(){
37294         return this.node.fireEvent.apply(this.node, arguments);
37295     },
37296
37297     initEvents : function(){
37298         this.node.on("move", this.onMove, this);
37299         var E = Roo.EventManager;
37300         var a = this.anchor;
37301
37302         var el = Roo.fly(a, '_treeui');
37303
37304         if(Roo.isOpera){ // opera render bug ignores the CSS
37305             el.setStyle("text-decoration", "none");
37306         }
37307
37308         el.on("click", this.onClick, this);
37309         el.on("dblclick", this.onDblClick, this);
37310
37311         if(this.checkbox){
37312             Roo.EventManager.on(this.checkbox,
37313                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37314         }
37315
37316         el.on("contextmenu", this.onContextMenu, this);
37317
37318         var icon = Roo.fly(this.iconNode);
37319         icon.on("click", this.onClick, this);
37320         icon.on("dblclick", this.onDblClick, this);
37321         icon.on("contextmenu", this.onContextMenu, this);
37322         E.on(this.ecNode, "click", this.ecClick, this, true);
37323
37324         if(this.node.disabled){
37325             this.addClass("x-tree-node-disabled");
37326         }
37327         if(this.node.hidden){
37328             this.addClass("x-tree-node-disabled");
37329         }
37330         var ot = this.node.getOwnerTree();
37331         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37332         if(dd && (!this.node.isRoot || ot.rootVisible)){
37333             Roo.dd.Registry.register(this.elNode, {
37334                 node: this.node,
37335                 handles: this.getDDHandles(),
37336                 isHandle: false
37337             });
37338         }
37339     },
37340
37341     getDDHandles : function(){
37342         return [this.iconNode, this.textNode];
37343     },
37344
37345     hide : function(){
37346         if(this.rendered){
37347             this.wrap.style.display = "none";
37348         }
37349     },
37350
37351     show : function(){
37352         if(this.rendered){
37353             this.wrap.style.display = "";
37354         }
37355     },
37356
37357     onContextMenu : function(e){
37358         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37359             e.preventDefault();
37360             this.focus();
37361             this.fireEvent("contextmenu", this.node, e);
37362         }
37363     },
37364
37365     onClick : function(e){
37366         if(this.dropping){
37367             e.stopEvent();
37368             return;
37369         }
37370         if(this.fireEvent("beforeclick", this.node, e) !== false){
37371             if(!this.disabled && this.node.attributes.href){
37372                 this.fireEvent("click", this.node, e);
37373                 return;
37374             }
37375             e.preventDefault();
37376             if(this.disabled){
37377                 return;
37378             }
37379
37380             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37381                 this.node.toggle();
37382             }
37383
37384             this.fireEvent("click", this.node, e);
37385         }else{
37386             e.stopEvent();
37387         }
37388     },
37389
37390     onDblClick : function(e){
37391         e.preventDefault();
37392         if(this.disabled){
37393             return;
37394         }
37395         if(this.checkbox){
37396             this.toggleCheck();
37397         }
37398         if(!this.animating && this.node.hasChildNodes()){
37399             this.node.toggle();
37400         }
37401         this.fireEvent("dblclick", this.node, e);
37402     },
37403
37404     onCheckChange : function(){
37405         var checked = this.checkbox.checked;
37406         this.node.attributes.checked = checked;
37407         this.fireEvent('checkchange', this.node, checked);
37408     },
37409
37410     ecClick : function(e){
37411         if(!this.animating && this.node.hasChildNodes()){
37412             this.node.toggle();
37413         }
37414     },
37415
37416     startDrop : function(){
37417         this.dropping = true;
37418     },
37419
37420     // delayed drop so the click event doesn't get fired on a drop
37421     endDrop : function(){
37422        setTimeout(function(){
37423            this.dropping = false;
37424        }.createDelegate(this), 50);
37425     },
37426
37427     expand : function(){
37428         this.updateExpandIcon();
37429         this.ctNode.style.display = "";
37430     },
37431
37432     focus : function(){
37433         if(!this.node.preventHScroll){
37434             try{this.anchor.focus();
37435             }catch(e){}
37436         }else if(!Roo.isIE){
37437             try{
37438                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37439                 var l = noscroll.scrollLeft;
37440                 this.anchor.focus();
37441                 noscroll.scrollLeft = l;
37442             }catch(e){}
37443         }
37444     },
37445
37446     toggleCheck : function(value){
37447         var cb = this.checkbox;
37448         if(cb){
37449             cb.checked = (value === undefined ? !cb.checked : value);
37450         }
37451     },
37452
37453     blur : function(){
37454         try{
37455             this.anchor.blur();
37456         }catch(e){}
37457     },
37458
37459     animExpand : function(callback){
37460         var ct = Roo.get(this.ctNode);
37461         ct.stopFx();
37462         if(!this.node.hasChildNodes()){
37463             this.updateExpandIcon();
37464             this.ctNode.style.display = "";
37465             Roo.callback(callback);
37466             return;
37467         }
37468         this.animating = true;
37469         this.updateExpandIcon();
37470
37471         ct.slideIn('t', {
37472            callback : function(){
37473                this.animating = false;
37474                Roo.callback(callback);
37475             },
37476             scope: this,
37477             duration: this.node.ownerTree.duration || .25
37478         });
37479     },
37480
37481     highlight : function(){
37482         var tree = this.node.getOwnerTree();
37483         Roo.fly(this.wrap).highlight(
37484             tree.hlColor || "C3DAF9",
37485             {endColor: tree.hlBaseColor}
37486         );
37487     },
37488
37489     collapse : function(){
37490         this.updateExpandIcon();
37491         this.ctNode.style.display = "none";
37492     },
37493
37494     animCollapse : function(callback){
37495         var ct = Roo.get(this.ctNode);
37496         ct.enableDisplayMode('block');
37497         ct.stopFx();
37498
37499         this.animating = true;
37500         this.updateExpandIcon();
37501
37502         ct.slideOut('t', {
37503             callback : function(){
37504                this.animating = false;
37505                Roo.callback(callback);
37506             },
37507             scope: this,
37508             duration: this.node.ownerTree.duration || .25
37509         });
37510     },
37511
37512     getContainer : function(){
37513         return this.ctNode;
37514     },
37515
37516     getEl : function(){
37517         return this.wrap;
37518     },
37519
37520     appendDDGhost : function(ghostNode){
37521         ghostNode.appendChild(this.elNode.cloneNode(true));
37522     },
37523
37524     getDDRepairXY : function(){
37525         return Roo.lib.Dom.getXY(this.iconNode);
37526     },
37527
37528     onRender : function(){
37529         this.render();
37530     },
37531
37532     render : function(bulkRender){
37533         var n = this.node, a = n.attributes;
37534         var targetNode = n.parentNode ?
37535               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37536
37537         if(!this.rendered){
37538             this.rendered = true;
37539
37540             this.renderElements(n, a, targetNode, bulkRender);
37541
37542             if(a.qtip){
37543                if(this.textNode.setAttributeNS){
37544                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37545                    if(a.qtipTitle){
37546                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37547                    }
37548                }else{
37549                    this.textNode.setAttribute("ext:qtip", a.qtip);
37550                    if(a.qtipTitle){
37551                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37552                    }
37553                }
37554             }else if(a.qtipCfg){
37555                 a.qtipCfg.target = Roo.id(this.textNode);
37556                 Roo.QuickTips.register(a.qtipCfg);
37557             }
37558             this.initEvents();
37559             if(!this.node.expanded){
37560                 this.updateExpandIcon();
37561             }
37562         }else{
37563             if(bulkRender === true) {
37564                 targetNode.appendChild(this.wrap);
37565             }
37566         }
37567     },
37568
37569     renderElements : function(n, a, targetNode, bulkRender)
37570     {
37571         // add some indent caching, this helps performance when rendering a large tree
37572         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37573         var t = n.getOwnerTree();
37574         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37575         if (typeof(n.attributes.html) != 'undefined') {
37576             txt = n.attributes.html;
37577         }
37578         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37579         var cb = typeof a.checked == 'boolean';
37580         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37581         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37582             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37583             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37584             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37585             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37586             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37587              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37588                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37589             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37590             "</li>"];
37591
37592         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37593             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37594                                 n.nextSibling.ui.getEl(), buf.join(""));
37595         }else{
37596             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37597         }
37598
37599         this.elNode = this.wrap.childNodes[0];
37600         this.ctNode = this.wrap.childNodes[1];
37601         var cs = this.elNode.childNodes;
37602         this.indentNode = cs[0];
37603         this.ecNode = cs[1];
37604         this.iconNode = cs[2];
37605         var index = 3;
37606         if(cb){
37607             this.checkbox = cs[3];
37608             index++;
37609         }
37610         this.anchor = cs[index];
37611         this.textNode = cs[index].firstChild;
37612     },
37613
37614     getAnchor : function(){
37615         return this.anchor;
37616     },
37617
37618     getTextEl : function(){
37619         return this.textNode;
37620     },
37621
37622     getIconEl : function(){
37623         return this.iconNode;
37624     },
37625
37626     isChecked : function(){
37627         return this.checkbox ? this.checkbox.checked : false;
37628     },
37629
37630     updateExpandIcon : function(){
37631         if(this.rendered){
37632             var n = this.node, c1, c2;
37633             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37634             var hasChild = n.hasChildNodes();
37635             if(hasChild){
37636                 if(n.expanded){
37637                     cls += "-minus";
37638                     c1 = "x-tree-node-collapsed";
37639                     c2 = "x-tree-node-expanded";
37640                 }else{
37641                     cls += "-plus";
37642                     c1 = "x-tree-node-expanded";
37643                     c2 = "x-tree-node-collapsed";
37644                 }
37645                 if(this.wasLeaf){
37646                     this.removeClass("x-tree-node-leaf");
37647                     this.wasLeaf = false;
37648                 }
37649                 if(this.c1 != c1 || this.c2 != c2){
37650                     Roo.fly(this.elNode).replaceClass(c1, c2);
37651                     this.c1 = c1; this.c2 = c2;
37652                 }
37653             }else{
37654                 // this changes non-leafs into leafs if they have no children.
37655                 // it's not very rational behaviour..
37656                 
37657                 if(!this.wasLeaf && this.node.leaf){
37658                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37659                     delete this.c1;
37660                     delete this.c2;
37661                     this.wasLeaf = true;
37662                 }
37663             }
37664             var ecc = "x-tree-ec-icon "+cls;
37665             if(this.ecc != ecc){
37666                 this.ecNode.className = ecc;
37667                 this.ecc = ecc;
37668             }
37669         }
37670     },
37671
37672     getChildIndent : function(){
37673         if(!this.childIndent){
37674             var buf = [];
37675             var p = this.node;
37676             while(p){
37677                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37678                     if(!p.isLast()) {
37679                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37680                     } else {
37681                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37682                     }
37683                 }
37684                 p = p.parentNode;
37685             }
37686             this.childIndent = buf.join("");
37687         }
37688         return this.childIndent;
37689     },
37690
37691     renderIndent : function(){
37692         if(this.rendered){
37693             var indent = "";
37694             var p = this.node.parentNode;
37695             if(p){
37696                 indent = p.ui.getChildIndent();
37697             }
37698             if(this.indentMarkup != indent){ // don't rerender if not required
37699                 this.indentNode.innerHTML = indent;
37700                 this.indentMarkup = indent;
37701             }
37702             this.updateExpandIcon();
37703         }
37704     }
37705 };
37706
37707 Roo.tree.RootTreeNodeUI = function(){
37708     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37709 };
37710 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37711     render : function(){
37712         if(!this.rendered){
37713             var targetNode = this.node.ownerTree.innerCt.dom;
37714             this.node.expanded = true;
37715             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37716             this.wrap = this.ctNode = targetNode.firstChild;
37717         }
37718     },
37719     collapse : function(){
37720     },
37721     expand : function(){
37722     }
37723 });/*
37724  * Based on:
37725  * Ext JS Library 1.1.1
37726  * Copyright(c) 2006-2007, Ext JS, LLC.
37727  *
37728  * Originally Released Under LGPL - original licence link has changed is not relivant.
37729  *
37730  * Fork - LGPL
37731  * <script type="text/javascript">
37732  */
37733 /**
37734  * @class Roo.tree.TreeLoader
37735  * @extends Roo.util.Observable
37736  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37737  * nodes from a specified URL. The response must be a javascript Array definition
37738  * who's elements are node definition objects. eg:
37739  * <pre><code>
37740 {  success : true,
37741    data :      [
37742    
37743     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37744     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37745     ]
37746 }
37747
37748
37749 </code></pre>
37750  * <br><br>
37751  * The old style respose with just an array is still supported, but not recommended.
37752  * <br><br>
37753  *
37754  * A server request is sent, and child nodes are loaded only when a node is expanded.
37755  * The loading node's id is passed to the server under the parameter name "node" to
37756  * enable the server to produce the correct child nodes.
37757  * <br><br>
37758  * To pass extra parameters, an event handler may be attached to the "beforeload"
37759  * event, and the parameters specified in the TreeLoader's baseParams property:
37760  * <pre><code>
37761     myTreeLoader.on("beforeload", function(treeLoader, node) {
37762         this.baseParams.category = node.attributes.category;
37763     }, this);
37764     
37765 </code></pre>
37766  *
37767  * This would pass an HTTP parameter called "category" to the server containing
37768  * the value of the Node's "category" attribute.
37769  * @constructor
37770  * Creates a new Treeloader.
37771  * @param {Object} config A config object containing config properties.
37772  */
37773 Roo.tree.TreeLoader = function(config){
37774     this.baseParams = {};
37775     this.requestMethod = "POST";
37776     Roo.apply(this, config);
37777
37778     this.addEvents({
37779     
37780         /**
37781          * @event beforeload
37782          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37783          * @param {Object} This TreeLoader object.
37784          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37785          * @param {Object} callback The callback function specified in the {@link #load} call.
37786          */
37787         beforeload : true,
37788         /**
37789          * @event load
37790          * Fires when the node has been successfuly loaded.
37791          * @param {Object} This TreeLoader object.
37792          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37793          * @param {Object} response The response object containing the data from the server.
37794          */
37795         load : true,
37796         /**
37797          * @event loadexception
37798          * Fires if the network request failed.
37799          * @param {Object} This TreeLoader object.
37800          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37801          * @param {Object} response The response object containing the data from the server.
37802          */
37803         loadexception : true,
37804         /**
37805          * @event create
37806          * Fires before a node is created, enabling you to return custom Node types 
37807          * @param {Object} This TreeLoader object.
37808          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37809          */
37810         create : true
37811     });
37812
37813     Roo.tree.TreeLoader.superclass.constructor.call(this);
37814 };
37815
37816 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37817     /**
37818     * @cfg {String} dataUrl The URL from which to request a Json string which
37819     * specifies an array of node definition object representing the child nodes
37820     * to be loaded.
37821     */
37822     /**
37823     * @cfg {String} requestMethod either GET or POST
37824     * defaults to POST (due to BC)
37825     * to be loaded.
37826     */
37827     /**
37828     * @cfg {Object} baseParams (optional) An object containing properties which
37829     * specify HTTP parameters to be passed to each request for child nodes.
37830     */
37831     /**
37832     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37833     * created by this loader. If the attributes sent by the server have an attribute in this object,
37834     * they take priority.
37835     */
37836     /**
37837     * @cfg {Object} uiProviders (optional) An object containing properties which
37838     * 
37839     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37840     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37841     * <i>uiProvider</i> attribute of a returned child node is a string rather
37842     * than a reference to a TreeNodeUI implementation, this that string value
37843     * is used as a property name in the uiProviders object. You can define the provider named
37844     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37845     */
37846     uiProviders : {},
37847
37848     /**
37849     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37850     * child nodes before loading.
37851     */
37852     clearOnLoad : true,
37853
37854     /**
37855     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37856     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37857     * Grid query { data : [ .....] }
37858     */
37859     
37860     root : false,
37861      /**
37862     * @cfg {String} queryParam (optional) 
37863     * Name of the query as it will be passed on the querystring (defaults to 'node')
37864     * eg. the request will be ?node=[id]
37865     */
37866     
37867     
37868     queryParam: false,
37869     
37870     /**
37871      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37872      * This is called automatically when a node is expanded, but may be used to reload
37873      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37874      * @param {Roo.tree.TreeNode} node
37875      * @param {Function} callback
37876      */
37877     load : function(node, callback){
37878         if(this.clearOnLoad){
37879             while(node.firstChild){
37880                 node.removeChild(node.firstChild);
37881             }
37882         }
37883         if(node.attributes.children){ // preloaded json children
37884             var cs = node.attributes.children;
37885             for(var i = 0, len = cs.length; i < len; i++){
37886                 node.appendChild(this.createNode(cs[i]));
37887             }
37888             if(typeof callback == "function"){
37889                 callback();
37890             }
37891         }else if(this.dataUrl){
37892             this.requestData(node, callback);
37893         }
37894     },
37895
37896     getParams: function(node){
37897         var buf = [], bp = this.baseParams;
37898         for(var key in bp){
37899             if(typeof bp[key] != "function"){
37900                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37901             }
37902         }
37903         var n = this.queryParam === false ? 'node' : this.queryParam;
37904         buf.push(n + "=", encodeURIComponent(node.id));
37905         return buf.join("");
37906     },
37907
37908     requestData : function(node, callback){
37909         if(this.fireEvent("beforeload", this, node, callback) !== false){
37910             this.transId = Roo.Ajax.request({
37911                 method:this.requestMethod,
37912                 url: this.dataUrl||this.url,
37913                 success: this.handleResponse,
37914                 failure: this.handleFailure,
37915                 scope: this,
37916                 argument: {callback: callback, node: node},
37917                 params: this.getParams(node)
37918             });
37919         }else{
37920             // if the load is cancelled, make sure we notify
37921             // the node that we are done
37922             if(typeof callback == "function"){
37923                 callback();
37924             }
37925         }
37926     },
37927
37928     isLoading : function(){
37929         return this.transId ? true : false;
37930     },
37931
37932     abort : function(){
37933         if(this.isLoading()){
37934             Roo.Ajax.abort(this.transId);
37935         }
37936     },
37937
37938     // private
37939     createNode : function(attr)
37940     {
37941         // apply baseAttrs, nice idea Corey!
37942         if(this.baseAttrs){
37943             Roo.applyIf(attr, this.baseAttrs);
37944         }
37945         if(this.applyLoader !== false){
37946             attr.loader = this;
37947         }
37948         // uiProvider = depreciated..
37949         
37950         if(typeof(attr.uiProvider) == 'string'){
37951            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37952                 /**  eval:var:attr */ eval(attr.uiProvider);
37953         }
37954         if(typeof(this.uiProviders['default']) != 'undefined') {
37955             attr.uiProvider = this.uiProviders['default'];
37956         }
37957         
37958         this.fireEvent('create', this, attr);
37959         
37960         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37961         return(attr.leaf ?
37962                         new Roo.tree.TreeNode(attr) :
37963                         new Roo.tree.AsyncTreeNode(attr));
37964     },
37965
37966     processResponse : function(response, node, callback)
37967     {
37968         var json = response.responseText;
37969         try {
37970             
37971             var o = Roo.decode(json);
37972             
37973             if (this.root === false && typeof(o.success) != undefined) {
37974                 this.root = 'data'; // the default behaviour for list like data..
37975                 }
37976                 
37977             if (this.root !== false &&  !o.success) {
37978                 // it's a failure condition.
37979                 var a = response.argument;
37980                 this.fireEvent("loadexception", this, a.node, response);
37981                 Roo.log("Load failed - should have a handler really");
37982                 return;
37983             }
37984             
37985             
37986             
37987             if (this.root !== false) {
37988                  o = o[this.root];
37989             }
37990             
37991             for(var i = 0, len = o.length; i < len; i++){
37992                 var n = this.createNode(o[i]);
37993                 if(n){
37994                     node.appendChild(n);
37995                 }
37996             }
37997             if(typeof callback == "function"){
37998                 callback(this, node);
37999             }
38000         }catch(e){
38001             this.handleFailure(response);
38002         }
38003     },
38004
38005     handleResponse : function(response){
38006         this.transId = false;
38007         var a = response.argument;
38008         this.processResponse(response, a.node, a.callback);
38009         this.fireEvent("load", this, a.node, response);
38010     },
38011
38012     handleFailure : function(response)
38013     {
38014         // should handle failure better..
38015         this.transId = false;
38016         var a = response.argument;
38017         this.fireEvent("loadexception", this, a.node, response);
38018         if(typeof a.callback == "function"){
38019             a.callback(this, a.node);
38020         }
38021     }
38022 });/*
38023  * Based on:
38024  * Ext JS Library 1.1.1
38025  * Copyright(c) 2006-2007, Ext JS, LLC.
38026  *
38027  * Originally Released Under LGPL - original licence link has changed is not relivant.
38028  *
38029  * Fork - LGPL
38030  * <script type="text/javascript">
38031  */
38032
38033 /**
38034 * @class Roo.tree.TreeFilter
38035 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38036 * @param {TreePanel} tree
38037 * @param {Object} config (optional)
38038  */
38039 Roo.tree.TreeFilter = function(tree, config){
38040     this.tree = tree;
38041     this.filtered = {};
38042     Roo.apply(this, config);
38043 };
38044
38045 Roo.tree.TreeFilter.prototype = {
38046     clearBlank:false,
38047     reverse:false,
38048     autoClear:false,
38049     remove:false,
38050
38051      /**
38052      * Filter the data by a specific attribute.
38053      * @param {String/RegExp} value Either string that the attribute value
38054      * should start with or a RegExp to test against the attribute
38055      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38056      * @param {TreeNode} startNode (optional) The node to start the filter at.
38057      */
38058     filter : function(value, attr, startNode){
38059         attr = attr || "text";
38060         var f;
38061         if(typeof value == "string"){
38062             var vlen = value.length;
38063             // auto clear empty filter
38064             if(vlen == 0 && this.clearBlank){
38065                 this.clear();
38066                 return;
38067             }
38068             value = value.toLowerCase();
38069             f = function(n){
38070                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38071             };
38072         }else if(value.exec){ // regex?
38073             f = function(n){
38074                 return value.test(n.attributes[attr]);
38075             };
38076         }else{
38077             throw 'Illegal filter type, must be string or regex';
38078         }
38079         this.filterBy(f, null, startNode);
38080         },
38081
38082     /**
38083      * Filter by a function. The passed function will be called with each
38084      * node in the tree (or from the startNode). If the function returns true, the node is kept
38085      * otherwise it is filtered. If a node is filtered, its children are also filtered.
38086      * @param {Function} fn The filter function
38087      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38088      */
38089     filterBy : function(fn, scope, startNode){
38090         startNode = startNode || this.tree.root;
38091         if(this.autoClear){
38092             this.clear();
38093         }
38094         var af = this.filtered, rv = this.reverse;
38095         var f = function(n){
38096             if(n == startNode){
38097                 return true;
38098             }
38099             if(af[n.id]){
38100                 return false;
38101             }
38102             var m = fn.call(scope || n, n);
38103             if(!m || rv){
38104                 af[n.id] = n;
38105                 n.ui.hide();
38106                 return false;
38107             }
38108             return true;
38109         };
38110         startNode.cascade(f);
38111         if(this.remove){
38112            for(var id in af){
38113                if(typeof id != "function"){
38114                    var n = af[id];
38115                    if(n && n.parentNode){
38116                        n.parentNode.removeChild(n);
38117                    }
38118                }
38119            }
38120         }
38121     },
38122
38123     /**
38124      * Clears the current filter. Note: with the "remove" option
38125      * set a filter cannot be cleared.
38126      */
38127     clear : function(){
38128         var t = this.tree;
38129         var af = this.filtered;
38130         for(var id in af){
38131             if(typeof id != "function"){
38132                 var n = af[id];
38133                 if(n){
38134                     n.ui.show();
38135                 }
38136             }
38137         }
38138         this.filtered = {};
38139     }
38140 };
38141 /*
38142  * Based on:
38143  * Ext JS Library 1.1.1
38144  * Copyright(c) 2006-2007, Ext JS, LLC.
38145  *
38146  * Originally Released Under LGPL - original licence link has changed is not relivant.
38147  *
38148  * Fork - LGPL
38149  * <script type="text/javascript">
38150  */
38151  
38152
38153 /**
38154  * @class Roo.tree.TreeSorter
38155  * Provides sorting of nodes in a TreePanel
38156  * 
38157  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38158  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38159  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38160  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38161  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38162  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38163  * @constructor
38164  * @param {TreePanel} tree
38165  * @param {Object} config
38166  */
38167 Roo.tree.TreeSorter = function(tree, config){
38168     Roo.apply(this, config);
38169     tree.on("beforechildrenrendered", this.doSort, this);
38170     tree.on("append", this.updateSort, this);
38171     tree.on("insert", this.updateSort, this);
38172     
38173     var dsc = this.dir && this.dir.toLowerCase() == "desc";
38174     var p = this.property || "text";
38175     var sortType = this.sortType;
38176     var fs = this.folderSort;
38177     var cs = this.caseSensitive === true;
38178     var leafAttr = this.leafAttr || 'leaf';
38179
38180     this.sortFn = function(n1, n2){
38181         if(fs){
38182             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38183                 return 1;
38184             }
38185             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38186                 return -1;
38187             }
38188         }
38189         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38190         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38191         if(v1 < v2){
38192                         return dsc ? +1 : -1;
38193                 }else if(v1 > v2){
38194                         return dsc ? -1 : +1;
38195         }else{
38196                 return 0;
38197         }
38198     };
38199 };
38200
38201 Roo.tree.TreeSorter.prototype = {
38202     doSort : function(node){
38203         node.sort(this.sortFn);
38204     },
38205     
38206     compareNodes : function(n1, n2){
38207         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38208     },
38209     
38210     updateSort : function(tree, node){
38211         if(node.childrenRendered){
38212             this.doSort.defer(1, this, [node]);
38213         }
38214     }
38215 };/*
38216  * Based on:
38217  * Ext JS Library 1.1.1
38218  * Copyright(c) 2006-2007, Ext JS, LLC.
38219  *
38220  * Originally Released Under LGPL - original licence link has changed is not relivant.
38221  *
38222  * Fork - LGPL
38223  * <script type="text/javascript">
38224  */
38225
38226 if(Roo.dd.DropZone){
38227     
38228 Roo.tree.TreeDropZone = function(tree, config){
38229     this.allowParentInsert = false;
38230     this.allowContainerDrop = false;
38231     this.appendOnly = false;
38232     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38233     this.tree = tree;
38234     this.lastInsertClass = "x-tree-no-status";
38235     this.dragOverData = {};
38236 };
38237
38238 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38239     ddGroup : "TreeDD",
38240     scroll:  true,
38241     
38242     expandDelay : 1000,
38243     
38244     expandNode : function(node){
38245         if(node.hasChildNodes() && !node.isExpanded()){
38246             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38247         }
38248     },
38249     
38250     queueExpand : function(node){
38251         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38252     },
38253     
38254     cancelExpand : function(){
38255         if(this.expandProcId){
38256             clearTimeout(this.expandProcId);
38257             this.expandProcId = false;
38258         }
38259     },
38260     
38261     isValidDropPoint : function(n, pt, dd, e, data){
38262         if(!n || !data){ return false; }
38263         var targetNode = n.node;
38264         var dropNode = data.node;
38265         // default drop rules
38266         if(!(targetNode && targetNode.isTarget && pt)){
38267             return false;
38268         }
38269         if(pt == "append" && targetNode.allowChildren === false){
38270             return false;
38271         }
38272         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38273             return false;
38274         }
38275         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38276             return false;
38277         }
38278         // reuse the object
38279         var overEvent = this.dragOverData;
38280         overEvent.tree = this.tree;
38281         overEvent.target = targetNode;
38282         overEvent.data = data;
38283         overEvent.point = pt;
38284         overEvent.source = dd;
38285         overEvent.rawEvent = e;
38286         overEvent.dropNode = dropNode;
38287         overEvent.cancel = false;  
38288         var result = this.tree.fireEvent("nodedragover", overEvent);
38289         return overEvent.cancel === false && result !== false;
38290     },
38291     
38292     getDropPoint : function(e, n, dd)
38293     {
38294         var tn = n.node;
38295         if(tn.isRoot){
38296             return tn.allowChildren !== false ? "append" : false; // always append for root
38297         }
38298         var dragEl = n.ddel;
38299         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38300         var y = Roo.lib.Event.getPageY(e);
38301         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38302         
38303         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38304         var noAppend = tn.allowChildren === false;
38305         if(this.appendOnly || tn.parentNode.allowChildren === false){
38306             return noAppend ? false : "append";
38307         }
38308         var noBelow = false;
38309         if(!this.allowParentInsert){
38310             noBelow = tn.hasChildNodes() && tn.isExpanded();
38311         }
38312         var q = (b - t) / (noAppend ? 2 : 3);
38313         if(y >= t && y < (t + q)){
38314             return "above";
38315         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38316             return "below";
38317         }else{
38318             return "append";
38319         }
38320     },
38321     
38322     onNodeEnter : function(n, dd, e, data)
38323     {
38324         this.cancelExpand();
38325     },
38326     
38327     onNodeOver : function(n, dd, e, data)
38328     {
38329        
38330         var pt = this.getDropPoint(e, n, dd);
38331         var node = n.node;
38332         
38333         // auto node expand check
38334         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38335             this.queueExpand(node);
38336         }else if(pt != "append"){
38337             this.cancelExpand();
38338         }
38339         
38340         // set the insert point style on the target node
38341         var returnCls = this.dropNotAllowed;
38342         if(this.isValidDropPoint(n, pt, dd, e, data)){
38343            if(pt){
38344                var el = n.ddel;
38345                var cls;
38346                if(pt == "above"){
38347                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38348                    cls = "x-tree-drag-insert-above";
38349                }else if(pt == "below"){
38350                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38351                    cls = "x-tree-drag-insert-below";
38352                }else{
38353                    returnCls = "x-tree-drop-ok-append";
38354                    cls = "x-tree-drag-append";
38355                }
38356                if(this.lastInsertClass != cls){
38357                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38358                    this.lastInsertClass = cls;
38359                }
38360            }
38361        }
38362        return returnCls;
38363     },
38364     
38365     onNodeOut : function(n, dd, e, data){
38366         
38367         this.cancelExpand();
38368         this.removeDropIndicators(n);
38369     },
38370     
38371     onNodeDrop : function(n, dd, e, data){
38372         var point = this.getDropPoint(e, n, dd);
38373         var targetNode = n.node;
38374         targetNode.ui.startDrop();
38375         if(!this.isValidDropPoint(n, point, dd, e, data)){
38376             targetNode.ui.endDrop();
38377             return false;
38378         }
38379         // first try to find the drop node
38380         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38381         var dropEvent = {
38382             tree : this.tree,
38383             target: targetNode,
38384             data: data,
38385             point: point,
38386             source: dd,
38387             rawEvent: e,
38388             dropNode: dropNode,
38389             cancel: !dropNode   
38390         };
38391         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38392         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38393             targetNode.ui.endDrop();
38394             return false;
38395         }
38396         // allow target changing
38397         targetNode = dropEvent.target;
38398         if(point == "append" && !targetNode.isExpanded()){
38399             targetNode.expand(false, null, function(){
38400                 this.completeDrop(dropEvent);
38401             }.createDelegate(this));
38402         }else{
38403             this.completeDrop(dropEvent);
38404         }
38405         return true;
38406     },
38407     
38408     completeDrop : function(de){
38409         var ns = de.dropNode, p = de.point, t = de.target;
38410         if(!(ns instanceof Array)){
38411             ns = [ns];
38412         }
38413         var n;
38414         for(var i = 0, len = ns.length; i < len; i++){
38415             n = ns[i];
38416             if(p == "above"){
38417                 t.parentNode.insertBefore(n, t);
38418             }else if(p == "below"){
38419                 t.parentNode.insertBefore(n, t.nextSibling);
38420             }else{
38421                 t.appendChild(n);
38422             }
38423         }
38424         n.ui.focus();
38425         if(this.tree.hlDrop){
38426             n.ui.highlight();
38427         }
38428         t.ui.endDrop();
38429         this.tree.fireEvent("nodedrop", de);
38430     },
38431     
38432     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38433         if(this.tree.hlDrop){
38434             dropNode.ui.focus();
38435             dropNode.ui.highlight();
38436         }
38437         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38438     },
38439     
38440     getTree : function(){
38441         return this.tree;
38442     },
38443     
38444     removeDropIndicators : function(n){
38445         if(n && n.ddel){
38446             var el = n.ddel;
38447             Roo.fly(el).removeClass([
38448                     "x-tree-drag-insert-above",
38449                     "x-tree-drag-insert-below",
38450                     "x-tree-drag-append"]);
38451             this.lastInsertClass = "_noclass";
38452         }
38453     },
38454     
38455     beforeDragDrop : function(target, e, id){
38456         this.cancelExpand();
38457         return true;
38458     },
38459     
38460     afterRepair : function(data){
38461         if(data && Roo.enableFx){
38462             data.node.ui.highlight();
38463         }
38464         this.hideProxy();
38465     } 
38466     
38467 });
38468
38469 }
38470 /*
38471  * Based on:
38472  * Ext JS Library 1.1.1
38473  * Copyright(c) 2006-2007, Ext JS, LLC.
38474  *
38475  * Originally Released Under LGPL - original licence link has changed is not relivant.
38476  *
38477  * Fork - LGPL
38478  * <script type="text/javascript">
38479  */
38480  
38481
38482 if(Roo.dd.DragZone){
38483 Roo.tree.TreeDragZone = function(tree, config){
38484     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38485     this.tree = tree;
38486 };
38487
38488 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38489     ddGroup : "TreeDD",
38490    
38491     onBeforeDrag : function(data, e){
38492         var n = data.node;
38493         return n && n.draggable && !n.disabled;
38494     },
38495      
38496     
38497     onInitDrag : function(e){
38498         var data = this.dragData;
38499         this.tree.getSelectionModel().select(data.node);
38500         this.proxy.update("");
38501         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38502         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38503     },
38504     
38505     getRepairXY : function(e, data){
38506         return data.node.ui.getDDRepairXY();
38507     },
38508     
38509     onEndDrag : function(data, e){
38510         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38511         
38512         
38513     },
38514     
38515     onValidDrop : function(dd, e, id){
38516         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38517         this.hideProxy();
38518     },
38519     
38520     beforeInvalidDrop : function(e, id){
38521         // this scrolls the original position back into view
38522         var sm = this.tree.getSelectionModel();
38523         sm.clearSelections();
38524         sm.select(this.dragData.node);
38525     }
38526 });
38527 }/*
38528  * Based on:
38529  * Ext JS Library 1.1.1
38530  * Copyright(c) 2006-2007, Ext JS, LLC.
38531  *
38532  * Originally Released Under LGPL - original licence link has changed is not relivant.
38533  *
38534  * Fork - LGPL
38535  * <script type="text/javascript">
38536  */
38537 /**
38538  * @class Roo.tree.TreeEditor
38539  * @extends Roo.Editor
38540  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38541  * as the editor field.
38542  * @constructor
38543  * @param {Object} config (used to be the tree panel.)
38544  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38545  * 
38546  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38547  * @cfg {Roo.form.TextField} field [required] The field configuration
38548  *
38549  * 
38550  */
38551 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38552     var tree = config;
38553     var field;
38554     if (oldconfig) { // old style..
38555         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38556     } else {
38557         // new style..
38558         tree = config.tree;
38559         config.field = config.field  || {};
38560         config.field.xtype = 'TextField';
38561         field = Roo.factory(config.field, Roo.form);
38562     }
38563     config = config || {};
38564     
38565     
38566     this.addEvents({
38567         /**
38568          * @event beforenodeedit
38569          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38570          * false from the handler of this event.
38571          * @param {Editor} this
38572          * @param {Roo.tree.Node} node 
38573          */
38574         "beforenodeedit" : true
38575     });
38576     
38577     //Roo.log(config);
38578     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38579
38580     this.tree = tree;
38581
38582     tree.on('beforeclick', this.beforeNodeClick, this);
38583     tree.getTreeEl().on('mousedown', this.hide, this);
38584     this.on('complete', this.updateNode, this);
38585     this.on('beforestartedit', this.fitToTree, this);
38586     this.on('startedit', this.bindScroll, this, {delay:10});
38587     this.on('specialkey', this.onSpecialKey, this);
38588 };
38589
38590 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38591     /**
38592      * @cfg {String} alignment
38593      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38594      */
38595     alignment: "l-l",
38596     // inherit
38597     autoSize: false,
38598     /**
38599      * @cfg {Boolean} hideEl
38600      * True to hide the bound element while the editor is displayed (defaults to false)
38601      */
38602     hideEl : false,
38603     /**
38604      * @cfg {String} cls
38605      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38606      */
38607     cls: "x-small-editor x-tree-editor",
38608     /**
38609      * @cfg {Boolean} shim
38610      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38611      */
38612     shim:false,
38613     // inherit
38614     shadow:"frame",
38615     /**
38616      * @cfg {Number} maxWidth
38617      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38618      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38619      * scroll and client offsets into account prior to each edit.
38620      */
38621     maxWidth: 250,
38622
38623     editDelay : 350,
38624
38625     // private
38626     fitToTree : function(ed, el){
38627         var td = this.tree.getTreeEl().dom, nd = el.dom;
38628         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38629             td.scrollLeft = nd.offsetLeft;
38630         }
38631         var w = Math.min(
38632                 this.maxWidth,
38633                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38634         this.setSize(w, '');
38635         
38636         return this.fireEvent('beforenodeedit', this, this.editNode);
38637         
38638     },
38639
38640     // private
38641     triggerEdit : function(node){
38642         this.completeEdit();
38643         this.editNode = node;
38644         this.startEdit(node.ui.textNode, node.text);
38645     },
38646
38647     // private
38648     bindScroll : function(){
38649         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38650     },
38651
38652     // private
38653     beforeNodeClick : function(node, e){
38654         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38655         this.lastClick = new Date();
38656         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38657             e.stopEvent();
38658             this.triggerEdit(node);
38659             return false;
38660         }
38661         return true;
38662     },
38663
38664     // private
38665     updateNode : function(ed, value){
38666         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38667         this.editNode.setText(value);
38668     },
38669
38670     // private
38671     onHide : function(){
38672         Roo.tree.TreeEditor.superclass.onHide.call(this);
38673         if(this.editNode){
38674             this.editNode.ui.focus();
38675         }
38676     },
38677
38678     // private
38679     onSpecialKey : function(field, e){
38680         var k = e.getKey();
38681         if(k == e.ESC){
38682             e.stopEvent();
38683             this.cancelEdit();
38684         }else if(k == e.ENTER && !e.hasModifier()){
38685             e.stopEvent();
38686             this.completeEdit();
38687         }
38688     }
38689 });//<Script type="text/javascript">
38690 /*
38691  * Based on:
38692  * Ext JS Library 1.1.1
38693  * Copyright(c) 2006-2007, Ext JS, LLC.
38694  *
38695  * Originally Released Under LGPL - original licence link has changed is not relivant.
38696  *
38697  * Fork - LGPL
38698  * <script type="text/javascript">
38699  */
38700  
38701 /**
38702  * Not documented??? - probably should be...
38703  */
38704
38705 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38706     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38707     
38708     renderElements : function(n, a, targetNode, bulkRender){
38709         //consel.log("renderElements?");
38710         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38711
38712         var t = n.getOwnerTree();
38713         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38714         
38715         var cols = t.columns;
38716         var bw = t.borderWidth;
38717         var c = cols[0];
38718         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38719          var cb = typeof a.checked == "boolean";
38720         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38721         var colcls = 'x-t-' + tid + '-c0';
38722         var buf = [
38723             '<li class="x-tree-node">',
38724             
38725                 
38726                 '<div class="x-tree-node-el ', a.cls,'">',
38727                     // extran...
38728                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38729                 
38730                 
38731                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38732                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38733                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38734                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38735                            (a.iconCls ? ' '+a.iconCls : ''),
38736                            '" unselectable="on" />',
38737                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38738                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38739                              
38740                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38741                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38742                             '<span unselectable="on" qtip="' + tx + '">',
38743                              tx,
38744                              '</span></a>' ,
38745                     '</div>',
38746                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38747                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38748                  ];
38749         for(var i = 1, len = cols.length; i < len; i++){
38750             c = cols[i];
38751             colcls = 'x-t-' + tid + '-c' +i;
38752             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38753             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38754                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38755                       "</div>");
38756          }
38757          
38758          buf.push(
38759             '</a>',
38760             '<div class="x-clear"></div></div>',
38761             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38762             "</li>");
38763         
38764         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38765             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38766                                 n.nextSibling.ui.getEl(), buf.join(""));
38767         }else{
38768             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38769         }
38770         var el = this.wrap.firstChild;
38771         this.elRow = el;
38772         this.elNode = el.firstChild;
38773         this.ranchor = el.childNodes[1];
38774         this.ctNode = this.wrap.childNodes[1];
38775         var cs = el.firstChild.childNodes;
38776         this.indentNode = cs[0];
38777         this.ecNode = cs[1];
38778         this.iconNode = cs[2];
38779         var index = 3;
38780         if(cb){
38781             this.checkbox = cs[3];
38782             index++;
38783         }
38784         this.anchor = cs[index];
38785         
38786         this.textNode = cs[index].firstChild;
38787         
38788         //el.on("click", this.onClick, this);
38789         //el.on("dblclick", this.onDblClick, this);
38790         
38791         
38792        // console.log(this);
38793     },
38794     initEvents : function(){
38795         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38796         
38797             
38798         var a = this.ranchor;
38799
38800         var el = Roo.get(a);
38801
38802         if(Roo.isOpera){ // opera render bug ignores the CSS
38803             el.setStyle("text-decoration", "none");
38804         }
38805
38806         el.on("click", this.onClick, this);
38807         el.on("dblclick", this.onDblClick, this);
38808         el.on("contextmenu", this.onContextMenu, this);
38809         
38810     },
38811     
38812     /*onSelectedChange : function(state){
38813         if(state){
38814             this.focus();
38815             this.addClass("x-tree-selected");
38816         }else{
38817             //this.blur();
38818             this.removeClass("x-tree-selected");
38819         }
38820     },*/
38821     addClass : function(cls){
38822         if(this.elRow){
38823             Roo.fly(this.elRow).addClass(cls);
38824         }
38825         
38826     },
38827     
38828     
38829     removeClass : function(cls){
38830         if(this.elRow){
38831             Roo.fly(this.elRow).removeClass(cls);
38832         }
38833     }
38834
38835     
38836     
38837 });//<Script type="text/javascript">
38838
38839 /*
38840  * Based on:
38841  * Ext JS Library 1.1.1
38842  * Copyright(c) 2006-2007, Ext JS, LLC.
38843  *
38844  * Originally Released Under LGPL - original licence link has changed is not relivant.
38845  *
38846  * Fork - LGPL
38847  * <script type="text/javascript">
38848  */
38849  
38850
38851 /**
38852  * @class Roo.tree.ColumnTree
38853  * @extends Roo.tree.TreePanel
38854  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38855  * @cfg {int} borderWidth  compined right/left border allowance
38856  * @constructor
38857  * @param {String/HTMLElement/Element} el The container element
38858  * @param {Object} config
38859  */
38860 Roo.tree.ColumnTree =  function(el, config)
38861 {
38862    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38863    this.addEvents({
38864         /**
38865         * @event resize
38866         * Fire this event on a container when it resizes
38867         * @param {int} w Width
38868         * @param {int} h Height
38869         */
38870        "resize" : true
38871     });
38872     this.on('resize', this.onResize, this);
38873 };
38874
38875 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38876     //lines:false,
38877     
38878     
38879     borderWidth: Roo.isBorderBox ? 0 : 2, 
38880     headEls : false,
38881     
38882     render : function(){
38883         // add the header.....
38884        
38885         Roo.tree.ColumnTree.superclass.render.apply(this);
38886         
38887         this.el.addClass('x-column-tree');
38888         
38889         this.headers = this.el.createChild(
38890             {cls:'x-tree-headers'},this.innerCt.dom);
38891    
38892         var cols = this.columns, c;
38893         var totalWidth = 0;
38894         this.headEls = [];
38895         var  len = cols.length;
38896         for(var i = 0; i < len; i++){
38897              c = cols[i];
38898              totalWidth += c.width;
38899             this.headEls.push(this.headers.createChild({
38900                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38901                  cn: {
38902                      cls:'x-tree-hd-text',
38903                      html: c.header
38904                  },
38905                  style:'width:'+(c.width-this.borderWidth)+'px;'
38906              }));
38907         }
38908         this.headers.createChild({cls:'x-clear'});
38909         // prevent floats from wrapping when clipped
38910         this.headers.setWidth(totalWidth);
38911         //this.innerCt.setWidth(totalWidth);
38912         this.innerCt.setStyle({ overflow: 'auto' });
38913         this.onResize(this.width, this.height);
38914              
38915         
38916     },
38917     onResize : function(w,h)
38918     {
38919         this.height = h;
38920         this.width = w;
38921         // resize cols..
38922         this.innerCt.setWidth(this.width);
38923         this.innerCt.setHeight(this.height-20);
38924         
38925         // headers...
38926         var cols = this.columns, c;
38927         var totalWidth = 0;
38928         var expEl = false;
38929         var len = cols.length;
38930         for(var i = 0; i < len; i++){
38931             c = cols[i];
38932             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38933                 // it's the expander..
38934                 expEl  = this.headEls[i];
38935                 continue;
38936             }
38937             totalWidth += c.width;
38938             
38939         }
38940         if (expEl) {
38941             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38942         }
38943         this.headers.setWidth(w-20);
38944
38945         
38946         
38947         
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.Menu
38963  * @extends Roo.util.Observable
38964  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38965  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38966  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38967  * @constructor
38968  * Creates a new Menu
38969  * @param {Object} config Configuration options
38970  */
38971 Roo.menu.Menu = function(config){
38972     
38973     Roo.menu.Menu.superclass.constructor.call(this, config);
38974     
38975     this.id = this.id || Roo.id();
38976     this.addEvents({
38977         /**
38978          * @event beforeshow
38979          * Fires before this menu is displayed
38980          * @param {Roo.menu.Menu} this
38981          */
38982         beforeshow : true,
38983         /**
38984          * @event beforehide
38985          * Fires before this menu is hidden
38986          * @param {Roo.menu.Menu} this
38987          */
38988         beforehide : true,
38989         /**
38990          * @event show
38991          * Fires after this menu is displayed
38992          * @param {Roo.menu.Menu} this
38993          */
38994         show : true,
38995         /**
38996          * @event hide
38997          * Fires after this menu is hidden
38998          * @param {Roo.menu.Menu} this
38999          */
39000         hide : true,
39001         /**
39002          * @event click
39003          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
39004          * @param {Roo.menu.Menu} this
39005          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39006          * @param {Roo.EventObject} e
39007          */
39008         click : true,
39009         /**
39010          * @event mouseover
39011          * Fires when the mouse is hovering over this menu
39012          * @param {Roo.menu.Menu} this
39013          * @param {Roo.EventObject} e
39014          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39015          */
39016         mouseover : true,
39017         /**
39018          * @event mouseout
39019          * Fires when the mouse exits this menu
39020          * @param {Roo.menu.Menu} this
39021          * @param {Roo.EventObject} e
39022          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39023          */
39024         mouseout : true,
39025         /**
39026          * @event itemclick
39027          * Fires when a menu item contained in this menu is clicked
39028          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39029          * @param {Roo.EventObject} e
39030          */
39031         itemclick: true
39032     });
39033     if (this.registerMenu) {
39034         Roo.menu.MenuMgr.register(this);
39035     }
39036     
39037     var mis = this.items;
39038     this.items = new Roo.util.MixedCollection();
39039     if(mis){
39040         this.add.apply(this, mis);
39041     }
39042 };
39043
39044 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39045     /**
39046      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39047      */
39048     minWidth : 120,
39049     /**
39050      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39051      * for bottom-right shadow (defaults to "sides")
39052      */
39053     shadow : "sides",
39054     /**
39055      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39056      * this menu (defaults to "tl-tr?")
39057      */
39058     subMenuAlign : "tl-tr?",
39059     /**
39060      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39061      * relative to its element of origin (defaults to "tl-bl?")
39062      */
39063     defaultAlign : "tl-bl?",
39064     /**
39065      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39066      */
39067     allowOtherMenus : false,
39068     /**
39069      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39070      */
39071     registerMenu : true,
39072
39073     hidden:true,
39074
39075     // private
39076     render : function(){
39077         if(this.el){
39078             return;
39079         }
39080         var el = this.el = new Roo.Layer({
39081             cls: "x-menu",
39082             shadow:this.shadow,
39083             constrain: false,
39084             parentEl: this.parentEl || document.body,
39085             zindex:15000
39086         });
39087
39088         this.keyNav = new Roo.menu.MenuNav(this);
39089
39090         if(this.plain){
39091             el.addClass("x-menu-plain");
39092         }
39093         if(this.cls){
39094             el.addClass(this.cls);
39095         }
39096         // generic focus element
39097         this.focusEl = el.createChild({
39098             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39099         });
39100         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39101         //disabling touch- as it's causing issues ..
39102         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
39103         ul.on('click'   , this.onClick, this);
39104         
39105         
39106         ul.on("mouseover", this.onMouseOver, this);
39107         ul.on("mouseout", this.onMouseOut, this);
39108         this.items.each(function(item){
39109             if (item.hidden) {
39110                 return;
39111             }
39112             
39113             var li = document.createElement("li");
39114             li.className = "x-menu-list-item";
39115             ul.dom.appendChild(li);
39116             item.render(li, this);
39117         }, this);
39118         this.ul = ul;
39119         this.autoWidth();
39120     },
39121
39122     // private
39123     autoWidth : function(){
39124         var el = this.el, ul = this.ul;
39125         if(!el){
39126             return;
39127         }
39128         var w = this.width;
39129         if(w){
39130             el.setWidth(w);
39131         }else if(Roo.isIE){
39132             el.setWidth(this.minWidth);
39133             var t = el.dom.offsetWidth; // force recalc
39134             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39135         }
39136     },
39137
39138     // private
39139     delayAutoWidth : function(){
39140         if(this.rendered){
39141             if(!this.awTask){
39142                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39143             }
39144             this.awTask.delay(20);
39145         }
39146     },
39147
39148     // private
39149     findTargetItem : function(e){
39150         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
39151         if(t && t.menuItemId){
39152             return this.items.get(t.menuItemId);
39153         }
39154     },
39155
39156     // private
39157     onClick : function(e){
39158         Roo.log("menu.onClick");
39159         var t = this.findTargetItem(e);
39160         if(!t){
39161             return;
39162         }
39163         Roo.log(e);
39164         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
39165             if(t == this.activeItem && t.shouldDeactivate(e)){
39166                 this.activeItem.deactivate();
39167                 delete this.activeItem;
39168                 return;
39169             }
39170             if(t.canActivate){
39171                 this.setActiveItem(t, true);
39172             }
39173             return;
39174             
39175             
39176         }
39177         
39178         t.onClick(e);
39179         this.fireEvent("click", this, t, e);
39180     },
39181
39182     // private
39183     setActiveItem : function(item, autoExpand){
39184         if(item != this.activeItem){
39185             if(this.activeItem){
39186                 this.activeItem.deactivate();
39187             }
39188             this.activeItem = item;
39189             item.activate(autoExpand);
39190         }else if(autoExpand){
39191             item.expandMenu();
39192         }
39193     },
39194
39195     // private
39196     tryActivate : function(start, step){
39197         var items = this.items;
39198         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39199             var item = items.get(i);
39200             if(!item.disabled && item.canActivate){
39201                 this.setActiveItem(item, false);
39202                 return item;
39203             }
39204         }
39205         return false;
39206     },
39207
39208     // private
39209     onMouseOver : function(e){
39210         var t;
39211         if(t = this.findTargetItem(e)){
39212             if(t.canActivate && !t.disabled){
39213                 this.setActiveItem(t, true);
39214             }
39215         }
39216         this.fireEvent("mouseover", this, e, t);
39217     },
39218
39219     // private
39220     onMouseOut : function(e){
39221         var t;
39222         if(t = this.findTargetItem(e)){
39223             if(t == this.activeItem && t.shouldDeactivate(e)){
39224                 this.activeItem.deactivate();
39225                 delete this.activeItem;
39226             }
39227         }
39228         this.fireEvent("mouseout", this, e, t);
39229     },
39230
39231     /**
39232      * Read-only.  Returns true if the menu is currently displayed, else false.
39233      * @type Boolean
39234      */
39235     isVisible : function(){
39236         return this.el && !this.hidden;
39237     },
39238
39239     /**
39240      * Displays this menu relative to another element
39241      * @param {String/HTMLElement/Roo.Element} element The element to align to
39242      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39243      * the element (defaults to this.defaultAlign)
39244      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39245      */
39246     show : function(el, pos, parentMenu){
39247         this.parentMenu = parentMenu;
39248         if(!this.el){
39249             this.render();
39250         }
39251         this.fireEvent("beforeshow", this);
39252         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39253     },
39254
39255     /**
39256      * Displays this menu at a specific xy position
39257      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39258      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39259      */
39260     showAt : function(xy, parentMenu, /* private: */_e){
39261         this.parentMenu = parentMenu;
39262         if(!this.el){
39263             this.render();
39264         }
39265         if(_e !== false){
39266             this.fireEvent("beforeshow", this);
39267             xy = this.el.adjustForConstraints(xy);
39268         }
39269         this.el.setXY(xy);
39270         this.el.show();
39271         this.hidden = false;
39272         this.focus();
39273         this.fireEvent("show", this);
39274     },
39275
39276     focus : function(){
39277         if(!this.hidden){
39278             this.doFocus.defer(50, this);
39279         }
39280     },
39281
39282     doFocus : function(){
39283         if(!this.hidden){
39284             this.focusEl.focus();
39285         }
39286     },
39287
39288     /**
39289      * Hides this menu and optionally all parent menus
39290      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39291      */
39292     hide : function(deep){
39293         if(this.el && this.isVisible()){
39294             this.fireEvent("beforehide", this);
39295             if(this.activeItem){
39296                 this.activeItem.deactivate();
39297                 this.activeItem = null;
39298             }
39299             this.el.hide();
39300             this.hidden = true;
39301             this.fireEvent("hide", this);
39302         }
39303         if(deep === true && this.parentMenu){
39304             this.parentMenu.hide(true);
39305         }
39306     },
39307
39308     /**
39309      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39310      * Any of the following are valid:
39311      * <ul>
39312      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39313      * <li>An HTMLElement object which will be converted to a menu item</li>
39314      * <li>A menu item config object that will be created as a new menu item</li>
39315      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39316      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39317      * </ul>
39318      * Usage:
39319      * <pre><code>
39320 // Create the menu
39321 var menu = new Roo.menu.Menu();
39322
39323 // Create a menu item to add by reference
39324 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39325
39326 // Add a bunch of items at once using different methods.
39327 // Only the last item added will be returned.
39328 var item = menu.add(
39329     menuItem,                // add existing item by ref
39330     'Dynamic Item',          // new TextItem
39331     '-',                     // new separator
39332     { text: 'Config Item' }  // new item by config
39333 );
39334 </code></pre>
39335      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39336      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39337      */
39338     add : function(){
39339         var a = arguments, l = a.length, item;
39340         for(var i = 0; i < l; i++){
39341             var el = a[i];
39342             if ((typeof(el) == "object") && el.xtype && el.xns) {
39343                 el = Roo.factory(el, Roo.menu);
39344             }
39345             
39346             if(el.render){ // some kind of Item
39347                 item = this.addItem(el);
39348             }else if(typeof el == "string"){ // string
39349                 if(el == "separator" || el == "-"){
39350                     item = this.addSeparator();
39351                 }else{
39352                     item = this.addText(el);
39353                 }
39354             }else if(el.tagName || el.el){ // element
39355                 item = this.addElement(el);
39356             }else if(typeof el == "object"){ // must be menu item config?
39357                 item = this.addMenuItem(el);
39358             }
39359         }
39360         return item;
39361     },
39362
39363     /**
39364      * Returns this menu's underlying {@link Roo.Element} object
39365      * @return {Roo.Element} The element
39366      */
39367     getEl : function(){
39368         if(!this.el){
39369             this.render();
39370         }
39371         return this.el;
39372     },
39373
39374     /**
39375      * Adds a separator bar to the menu
39376      * @return {Roo.menu.Item} The menu item that was added
39377      */
39378     addSeparator : function(){
39379         return this.addItem(new Roo.menu.Separator());
39380     },
39381
39382     /**
39383      * Adds an {@link Roo.Element} object to the menu
39384      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39385      * @return {Roo.menu.Item} The menu item that was added
39386      */
39387     addElement : function(el){
39388         return this.addItem(new Roo.menu.BaseItem(el));
39389     },
39390
39391     /**
39392      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39393      * @param {Roo.menu.Item} item The menu item to add
39394      * @return {Roo.menu.Item} The menu item that was added
39395      */
39396     addItem : function(item){
39397         this.items.add(item);
39398         if(this.ul){
39399             var li = document.createElement("li");
39400             li.className = "x-menu-list-item";
39401             this.ul.dom.appendChild(li);
39402             item.render(li, this);
39403             this.delayAutoWidth();
39404         }
39405         return item;
39406     },
39407
39408     /**
39409      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39410      * @param {Object} config A MenuItem config object
39411      * @return {Roo.menu.Item} The menu item that was added
39412      */
39413     addMenuItem : function(config){
39414         if(!(config instanceof Roo.menu.Item)){
39415             if(typeof config.checked == "boolean"){ // must be check menu item config?
39416                 config = new Roo.menu.CheckItem(config);
39417             }else{
39418                 config = new Roo.menu.Item(config);
39419             }
39420         }
39421         return this.addItem(config);
39422     },
39423
39424     /**
39425      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39426      * @param {String} text The text to display in the menu item
39427      * @return {Roo.menu.Item} The menu item that was added
39428      */
39429     addText : function(text){
39430         return this.addItem(new Roo.menu.TextItem({ text : text }));
39431     },
39432
39433     /**
39434      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39435      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39436      * @param {Roo.menu.Item} item The menu item to add
39437      * @return {Roo.menu.Item} The menu item that was added
39438      */
39439     insert : function(index, item){
39440         this.items.insert(index, item);
39441         if(this.ul){
39442             var li = document.createElement("li");
39443             li.className = "x-menu-list-item";
39444             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39445             item.render(li, this);
39446             this.delayAutoWidth();
39447         }
39448         return item;
39449     },
39450
39451     /**
39452      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39453      * @param {Roo.menu.Item} item The menu item to remove
39454      */
39455     remove : function(item){
39456         this.items.removeKey(item.id);
39457         item.destroy();
39458     },
39459
39460     /**
39461      * Removes and destroys all items in the menu
39462      */
39463     removeAll : function(){
39464         var f;
39465         while(f = this.items.first()){
39466             this.remove(f);
39467         }
39468     }
39469 });
39470
39471 // MenuNav is a private utility class used internally by the Menu
39472 Roo.menu.MenuNav = function(menu){
39473     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39474     this.scope = this.menu = menu;
39475 };
39476
39477 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39478     doRelay : function(e, h){
39479         var k = e.getKey();
39480         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39481             this.menu.tryActivate(0, 1);
39482             return false;
39483         }
39484         return h.call(this.scope || this, e, this.menu);
39485     },
39486
39487     up : function(e, m){
39488         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39489             m.tryActivate(m.items.length-1, -1);
39490         }
39491     },
39492
39493     down : function(e, m){
39494         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39495             m.tryActivate(0, 1);
39496         }
39497     },
39498
39499     right : function(e, m){
39500         if(m.activeItem){
39501             m.activeItem.expandMenu(true);
39502         }
39503     },
39504
39505     left : function(e, m){
39506         m.hide();
39507         if(m.parentMenu && m.parentMenu.activeItem){
39508             m.parentMenu.activeItem.activate();
39509         }
39510     },
39511
39512     enter : function(e, m){
39513         if(m.activeItem){
39514             e.stopPropagation();
39515             m.activeItem.onClick(e);
39516             m.fireEvent("click", this, m.activeItem);
39517             return true;
39518         }
39519     }
39520 });/*
39521  * Based on:
39522  * Ext JS Library 1.1.1
39523  * Copyright(c) 2006-2007, Ext JS, LLC.
39524  *
39525  * Originally Released Under LGPL - original licence link has changed is not relivant.
39526  *
39527  * Fork - LGPL
39528  * <script type="text/javascript">
39529  */
39530  
39531 /**
39532  * @class Roo.menu.MenuMgr
39533  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39534  * @static
39535  */
39536 Roo.menu.MenuMgr = function(){
39537    var menus, active, groups = {}, attached = false, lastShow = new Date();
39538
39539    // private - called when first menu is created
39540    function init(){
39541        menus = {};
39542        active = new Roo.util.MixedCollection();
39543        Roo.get(document).addKeyListener(27, function(){
39544            if(active.length > 0){
39545                hideAll();
39546            }
39547        });
39548    }
39549
39550    // private
39551    function hideAll(){
39552        if(active && active.length > 0){
39553            var c = active.clone();
39554            c.each(function(m){
39555                m.hide();
39556            });
39557        }
39558    }
39559
39560    // private
39561    function onHide(m){
39562        active.remove(m);
39563        if(active.length < 1){
39564            Roo.get(document).un("mousedown", onMouseDown);
39565            attached = false;
39566        }
39567    }
39568
39569    // private
39570    function onShow(m){
39571        var last = active.last();
39572        lastShow = new Date();
39573        active.add(m);
39574        if(!attached){
39575            Roo.get(document).on("mousedown", onMouseDown);
39576            attached = true;
39577        }
39578        if(m.parentMenu){
39579           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39580           m.parentMenu.activeChild = m;
39581        }else if(last && last.isVisible()){
39582           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39583        }
39584    }
39585
39586    // private
39587    function onBeforeHide(m){
39588        if(m.activeChild){
39589            m.activeChild.hide();
39590        }
39591        if(m.autoHideTimer){
39592            clearTimeout(m.autoHideTimer);
39593            delete m.autoHideTimer;
39594        }
39595    }
39596
39597    // private
39598    function onBeforeShow(m){
39599        var pm = m.parentMenu;
39600        if(!pm && !m.allowOtherMenus){
39601            hideAll();
39602        }else if(pm && pm.activeChild && active != m){
39603            pm.activeChild.hide();
39604        }
39605    }
39606
39607    // private
39608    function onMouseDown(e){
39609        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39610            hideAll();
39611        }
39612    }
39613
39614    // private
39615    function onBeforeCheck(mi, state){
39616        if(state){
39617            var g = groups[mi.group];
39618            for(var i = 0, l = g.length; i < l; i++){
39619                if(g[i] != mi){
39620                    g[i].setChecked(false);
39621                }
39622            }
39623        }
39624    }
39625
39626    return {
39627
39628        /**
39629         * Hides all menus that are currently visible
39630         */
39631        hideAll : function(){
39632             hideAll();  
39633        },
39634
39635        // private
39636        register : function(menu){
39637            if(!menus){
39638                init();
39639            }
39640            menus[menu.id] = menu;
39641            menu.on("beforehide", onBeforeHide);
39642            menu.on("hide", onHide);
39643            menu.on("beforeshow", onBeforeShow);
39644            menu.on("show", onShow);
39645            var g = menu.group;
39646            if(g && menu.events["checkchange"]){
39647                if(!groups[g]){
39648                    groups[g] = [];
39649                }
39650                groups[g].push(menu);
39651                menu.on("checkchange", onCheck);
39652            }
39653        },
39654
39655         /**
39656          * Returns a {@link Roo.menu.Menu} object
39657          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39658          * be used to generate and return a new Menu instance.
39659          */
39660        get : function(menu){
39661            if(typeof menu == "string"){ // menu id
39662                return menus[menu];
39663            }else if(menu.events){  // menu instance
39664                return menu;
39665            }else if(typeof menu.length == 'number'){ // array of menu items?
39666                return new Roo.menu.Menu({items:menu});
39667            }else{ // otherwise, must be a config
39668                return new Roo.menu.Menu(menu);
39669            }
39670        },
39671
39672        // private
39673        unregister : function(menu){
39674            delete menus[menu.id];
39675            menu.un("beforehide", onBeforeHide);
39676            menu.un("hide", onHide);
39677            menu.un("beforeshow", onBeforeShow);
39678            menu.un("show", onShow);
39679            var g = menu.group;
39680            if(g && menu.events["checkchange"]){
39681                groups[g].remove(menu);
39682                menu.un("checkchange", onCheck);
39683            }
39684        },
39685
39686        // private
39687        registerCheckable : function(menuItem){
39688            var g = menuItem.group;
39689            if(g){
39690                if(!groups[g]){
39691                    groups[g] = [];
39692                }
39693                groups[g].push(menuItem);
39694                menuItem.on("beforecheckchange", onBeforeCheck);
39695            }
39696        },
39697
39698        // private
39699        unregisterCheckable : function(menuItem){
39700            var g = menuItem.group;
39701            if(g){
39702                groups[g].remove(menuItem);
39703                menuItem.un("beforecheckchange", onBeforeCheck);
39704            }
39705        }
39706    };
39707 }();/*
39708  * Based on:
39709  * Ext JS Library 1.1.1
39710  * Copyright(c) 2006-2007, Ext JS, LLC.
39711  *
39712  * Originally Released Under LGPL - original licence link has changed is not relivant.
39713  *
39714  * Fork - LGPL
39715  * <script type="text/javascript">
39716  */
39717  
39718
39719 /**
39720  * @class Roo.menu.BaseItem
39721  * @extends Roo.Component
39722  * @abstract
39723  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39724  * management and base configuration options shared by all menu components.
39725  * @constructor
39726  * Creates a new BaseItem
39727  * @param {Object} config Configuration options
39728  */
39729 Roo.menu.BaseItem = function(config){
39730     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39731
39732     this.addEvents({
39733         /**
39734          * @event click
39735          * Fires when this item is clicked
39736          * @param {Roo.menu.BaseItem} this
39737          * @param {Roo.EventObject} e
39738          */
39739         click: true,
39740         /**
39741          * @event activate
39742          * Fires when this item is activated
39743          * @param {Roo.menu.BaseItem} this
39744          */
39745         activate : true,
39746         /**
39747          * @event deactivate
39748          * Fires when this item is deactivated
39749          * @param {Roo.menu.BaseItem} this
39750          */
39751         deactivate : true
39752     });
39753
39754     if(this.handler){
39755         this.on("click", this.handler, this.scope, true);
39756     }
39757 };
39758
39759 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39760     /**
39761      * @cfg {Function} handler
39762      * A function that will handle the click event of this menu item (defaults to undefined)
39763      */
39764     /**
39765      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39766      */
39767     canActivate : false,
39768     
39769      /**
39770      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39771      */
39772     hidden: false,
39773     
39774     /**
39775      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39776      */
39777     activeClass : "x-menu-item-active",
39778     /**
39779      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39780      */
39781     hideOnClick : true,
39782     /**
39783      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39784      */
39785     hideDelay : 100,
39786
39787     // private
39788     ctype: "Roo.menu.BaseItem",
39789
39790     // private
39791     actionMode : "container",
39792
39793     // private
39794     render : function(container, parentMenu){
39795         this.parentMenu = parentMenu;
39796         Roo.menu.BaseItem.superclass.render.call(this, container);
39797         this.container.menuItemId = this.id;
39798     },
39799
39800     // private
39801     onRender : function(container, position){
39802         this.el = Roo.get(this.el);
39803         container.dom.appendChild(this.el.dom);
39804     },
39805
39806     // private
39807     onClick : function(e){
39808         if(!this.disabled && this.fireEvent("click", this, e) !== false
39809                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39810             this.handleClick(e);
39811         }else{
39812             e.stopEvent();
39813         }
39814     },
39815
39816     // private
39817     activate : function(){
39818         if(this.disabled){
39819             return false;
39820         }
39821         var li = this.container;
39822         li.addClass(this.activeClass);
39823         this.region = li.getRegion().adjust(2, 2, -2, -2);
39824         this.fireEvent("activate", this);
39825         return true;
39826     },
39827
39828     // private
39829     deactivate : function(){
39830         this.container.removeClass(this.activeClass);
39831         this.fireEvent("deactivate", this);
39832     },
39833
39834     // private
39835     shouldDeactivate : function(e){
39836         return !this.region || !this.region.contains(e.getPoint());
39837     },
39838
39839     // private
39840     handleClick : function(e){
39841         if(this.hideOnClick){
39842             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39843         }
39844     },
39845
39846     // private
39847     expandMenu : function(autoActivate){
39848         // do nothing
39849     },
39850
39851     // private
39852     hideMenu : function(){
39853         // do nothing
39854     }
39855 });/*
39856  * Based on:
39857  * Ext JS Library 1.1.1
39858  * Copyright(c) 2006-2007, Ext JS, LLC.
39859  *
39860  * Originally Released Under LGPL - original licence link has changed is not relivant.
39861  *
39862  * Fork - LGPL
39863  * <script type="text/javascript">
39864  */
39865  
39866 /**
39867  * @class Roo.menu.Adapter
39868  * @extends Roo.menu.BaseItem
39869  * @abstract
39870  * 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.
39871  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39872  * @constructor
39873  * Creates a new Adapter
39874  * @param {Object} config Configuration options
39875  */
39876 Roo.menu.Adapter = function(component, config){
39877     Roo.menu.Adapter.superclass.constructor.call(this, config);
39878     this.component = component;
39879 };
39880 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39881     // private
39882     canActivate : true,
39883
39884     // private
39885     onRender : function(container, position){
39886         this.component.render(container);
39887         this.el = this.component.getEl();
39888     },
39889
39890     // private
39891     activate : function(){
39892         if(this.disabled){
39893             return false;
39894         }
39895         this.component.focus();
39896         this.fireEvent("activate", this);
39897         return true;
39898     },
39899
39900     // private
39901     deactivate : function(){
39902         this.fireEvent("deactivate", this);
39903     },
39904
39905     // private
39906     disable : function(){
39907         this.component.disable();
39908         Roo.menu.Adapter.superclass.disable.call(this);
39909     },
39910
39911     // private
39912     enable : function(){
39913         this.component.enable();
39914         Roo.menu.Adapter.superclass.enable.call(this);
39915     }
39916 });/*
39917  * Based on:
39918  * Ext JS Library 1.1.1
39919  * Copyright(c) 2006-2007, Ext JS, LLC.
39920  *
39921  * Originally Released Under LGPL - original licence link has changed is not relivant.
39922  *
39923  * Fork - LGPL
39924  * <script type="text/javascript">
39925  */
39926
39927 /**
39928  * @class Roo.menu.TextItem
39929  * @extends Roo.menu.BaseItem
39930  * Adds a static text string to a menu, usually used as either a heading or group separator.
39931  * Note: old style constructor with text is still supported.
39932  * 
39933  * @constructor
39934  * Creates a new TextItem
39935  * @param {Object} cfg Configuration
39936  */
39937 Roo.menu.TextItem = function(cfg){
39938     if (typeof(cfg) == 'string') {
39939         this.text = cfg;
39940     } else {
39941         Roo.apply(this,cfg);
39942     }
39943     
39944     Roo.menu.TextItem.superclass.constructor.call(this);
39945 };
39946
39947 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39948     /**
39949      * @cfg {String} text Text to show on item.
39950      */
39951     text : '',
39952     
39953     /**
39954      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39955      */
39956     hideOnClick : false,
39957     /**
39958      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39959      */
39960     itemCls : "x-menu-text",
39961
39962     // private
39963     onRender : function(){
39964         var s = document.createElement("span");
39965         s.className = this.itemCls;
39966         s.innerHTML = this.text;
39967         this.el = s;
39968         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39969     }
39970 });/*
39971  * Based on:
39972  * Ext JS Library 1.1.1
39973  * Copyright(c) 2006-2007, Ext JS, LLC.
39974  *
39975  * Originally Released Under LGPL - original licence link has changed is not relivant.
39976  *
39977  * Fork - LGPL
39978  * <script type="text/javascript">
39979  */
39980
39981 /**
39982  * @class Roo.menu.Separator
39983  * @extends Roo.menu.BaseItem
39984  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39985  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39986  * @constructor
39987  * @param {Object} config Configuration options
39988  */
39989 Roo.menu.Separator = function(config){
39990     Roo.menu.Separator.superclass.constructor.call(this, config);
39991 };
39992
39993 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39994     /**
39995      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39996      */
39997     itemCls : "x-menu-sep",
39998     /**
39999      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
40000      */
40001     hideOnClick : false,
40002
40003     // private
40004     onRender : function(li){
40005         var s = document.createElement("span");
40006         s.className = this.itemCls;
40007         s.innerHTML = "&#160;";
40008         this.el = s;
40009         li.addClass("x-menu-sep-li");
40010         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
40011     }
40012 });/*
40013  * Based on:
40014  * Ext JS Library 1.1.1
40015  * Copyright(c) 2006-2007, Ext JS, LLC.
40016  *
40017  * Originally Released Under LGPL - original licence link has changed is not relivant.
40018  *
40019  * Fork - LGPL
40020  * <script type="text/javascript">
40021  */
40022 /**
40023  * @class Roo.menu.Item
40024  * @extends Roo.menu.BaseItem
40025  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40026  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40027  * activation and click handling.
40028  * @constructor
40029  * Creates a new Item
40030  * @param {Object} config Configuration options
40031  */
40032 Roo.menu.Item = function(config){
40033     Roo.menu.Item.superclass.constructor.call(this, config);
40034     if(this.menu){
40035         this.menu = Roo.menu.MenuMgr.get(this.menu);
40036     }
40037 };
40038 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40039     /**
40040      * @cfg {Roo.menu.Menu} menu
40041      * A Sub menu
40042      */
40043     /**
40044      * @cfg {String} text
40045      * The text to show on the menu item.
40046      */
40047     text: '',
40048      /**
40049      * @cfg {String} html to render in menu
40050      * The text to show on the menu item (HTML version).
40051      */
40052     html: '',
40053     /**
40054      * @cfg {String} icon
40055      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40056      */
40057     icon: undefined,
40058     /**
40059      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40060      */
40061     itemCls : "x-menu-item",
40062     /**
40063      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40064      */
40065     canActivate : true,
40066     /**
40067      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40068      */
40069     showDelay: 200,
40070     // doc'd in BaseItem
40071     hideDelay: 200,
40072
40073     // private
40074     ctype: "Roo.menu.Item",
40075     
40076     // private
40077     onRender : function(container, position){
40078         var el = document.createElement("a");
40079         el.hideFocus = true;
40080         el.unselectable = "on";
40081         el.href = this.href || "#";
40082         if(this.hrefTarget){
40083             el.target = this.hrefTarget;
40084         }
40085         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
40086         
40087         var html = this.html.length ? this.html  : String.format('{0}',this.text);
40088         
40089         el.innerHTML = String.format(
40090                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40091                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40092         this.el = el;
40093         Roo.menu.Item.superclass.onRender.call(this, container, position);
40094     },
40095
40096     /**
40097      * Sets the text to display in this menu item
40098      * @param {String} text The text to display
40099      * @param {Boolean} isHTML true to indicate text is pure html.
40100      */
40101     setText : function(text, isHTML){
40102         if (isHTML) {
40103             this.html = text;
40104         } else {
40105             this.text = text;
40106             this.html = '';
40107         }
40108         if(this.rendered){
40109             var html = this.html.length ? this.html  : String.format('{0}',this.text);
40110      
40111             this.el.update(String.format(
40112                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40113                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40114             this.parentMenu.autoWidth();
40115         }
40116     },
40117
40118     // private
40119     handleClick : function(e){
40120         if(!this.href){ // if no link defined, stop the event automatically
40121             e.stopEvent();
40122         }
40123         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40124     },
40125
40126     // private
40127     activate : function(autoExpand){
40128         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40129             this.focus();
40130             if(autoExpand){
40131                 this.expandMenu();
40132             }
40133         }
40134         return true;
40135     },
40136
40137     // private
40138     shouldDeactivate : function(e){
40139         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40140             if(this.menu && this.menu.isVisible()){
40141                 return !this.menu.getEl().getRegion().contains(e.getPoint());
40142             }
40143             return true;
40144         }
40145         return false;
40146     },
40147
40148     // private
40149     deactivate : function(){
40150         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40151         this.hideMenu();
40152     },
40153
40154     // private
40155     expandMenu : function(autoActivate){
40156         if(!this.disabled && this.menu){
40157             clearTimeout(this.hideTimer);
40158             delete this.hideTimer;
40159             if(!this.menu.isVisible() && !this.showTimer){
40160                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40161             }else if (this.menu.isVisible() && autoActivate){
40162                 this.menu.tryActivate(0, 1);
40163             }
40164         }
40165     },
40166
40167     // private
40168     deferExpand : function(autoActivate){
40169         delete this.showTimer;
40170         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40171         if(autoActivate){
40172             this.menu.tryActivate(0, 1);
40173         }
40174     },
40175
40176     // private
40177     hideMenu : function(){
40178         clearTimeout(this.showTimer);
40179         delete this.showTimer;
40180         if(!this.hideTimer && this.menu && this.menu.isVisible()){
40181             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40182         }
40183     },
40184
40185     // private
40186     deferHide : function(){
40187         delete this.hideTimer;
40188         this.menu.hide();
40189     }
40190 });/*
40191  * Based on:
40192  * Ext JS Library 1.1.1
40193  * Copyright(c) 2006-2007, Ext JS, LLC.
40194  *
40195  * Originally Released Under LGPL - original licence link has changed is not relivant.
40196  *
40197  * Fork - LGPL
40198  * <script type="text/javascript">
40199  */
40200  
40201 /**
40202  * @class Roo.menu.CheckItem
40203  * @extends Roo.menu.Item
40204  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40205  * @constructor
40206  * Creates a new CheckItem
40207  * @param {Object} config Configuration options
40208  */
40209 Roo.menu.CheckItem = function(config){
40210     Roo.menu.CheckItem.superclass.constructor.call(this, config);
40211     this.addEvents({
40212         /**
40213          * @event beforecheckchange
40214          * Fires before the checked value is set, providing an opportunity to cancel if needed
40215          * @param {Roo.menu.CheckItem} this
40216          * @param {Boolean} checked The new checked value that will be set
40217          */
40218         "beforecheckchange" : true,
40219         /**
40220          * @event checkchange
40221          * Fires after the checked value has been set
40222          * @param {Roo.menu.CheckItem} this
40223          * @param {Boolean} checked The checked value that was set
40224          */
40225         "checkchange" : true
40226     });
40227     if(this.checkHandler){
40228         this.on('checkchange', this.checkHandler, this.scope);
40229     }
40230 };
40231 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40232     /**
40233      * @cfg {String} group
40234      * All check items with the same group name will automatically be grouped into a single-select
40235      * radio button group (defaults to '')
40236      */
40237     /**
40238      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40239      */
40240     itemCls : "x-menu-item x-menu-check-item",
40241     /**
40242      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40243      */
40244     groupClass : "x-menu-group-item",
40245
40246     /**
40247      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40248      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40249      * initialized with checked = true will be rendered as checked.
40250      */
40251     checked: false,
40252
40253     // private
40254     ctype: "Roo.menu.CheckItem",
40255
40256     // private
40257     onRender : function(c){
40258         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40259         if(this.group){
40260             this.el.addClass(this.groupClass);
40261         }
40262         Roo.menu.MenuMgr.registerCheckable(this);
40263         if(this.checked){
40264             this.checked = false;
40265             this.setChecked(true, true);
40266         }
40267     },
40268
40269     // private
40270     destroy : function(){
40271         if(this.rendered){
40272             Roo.menu.MenuMgr.unregisterCheckable(this);
40273         }
40274         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40275     },
40276
40277     /**
40278      * Set the checked state of this item
40279      * @param {Boolean} checked The new checked value
40280      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40281      */
40282     setChecked : function(state, suppressEvent){
40283         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40284             if(this.container){
40285                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40286             }
40287             this.checked = state;
40288             if(suppressEvent !== true){
40289                 this.fireEvent("checkchange", this, state);
40290             }
40291         }
40292     },
40293
40294     // private
40295     handleClick : function(e){
40296        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40297            this.setChecked(!this.checked);
40298        }
40299        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40300     }
40301 });/*
40302  * Based on:
40303  * Ext JS Library 1.1.1
40304  * Copyright(c) 2006-2007, Ext JS, LLC.
40305  *
40306  * Originally Released Under LGPL - original licence link has changed is not relivant.
40307  *
40308  * Fork - LGPL
40309  * <script type="text/javascript">
40310  */
40311  
40312 /**
40313  * @class Roo.menu.DateItem
40314  * @extends Roo.menu.Adapter
40315  * A menu item that wraps the {@link Roo.DatPicker} component.
40316  * @constructor
40317  * Creates a new DateItem
40318  * @param {Object} config Configuration options
40319  */
40320 Roo.menu.DateItem = function(config){
40321     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40322     /** The Roo.DatePicker object @type Roo.DatePicker */
40323     this.picker = this.component;
40324     this.addEvents({select: true});
40325     
40326     this.picker.on("render", function(picker){
40327         picker.getEl().swallowEvent("click");
40328         picker.container.addClass("x-menu-date-item");
40329     });
40330
40331     this.picker.on("select", this.onSelect, this);
40332 };
40333
40334 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40335     // private
40336     onSelect : function(picker, date){
40337         this.fireEvent("select", this, date, picker);
40338         Roo.menu.DateItem.superclass.handleClick.call(this);
40339     }
40340 });/*
40341  * Based on:
40342  * Ext JS Library 1.1.1
40343  * Copyright(c) 2006-2007, Ext JS, LLC.
40344  *
40345  * Originally Released Under LGPL - original licence link has changed is not relivant.
40346  *
40347  * Fork - LGPL
40348  * <script type="text/javascript">
40349  */
40350  
40351 /**
40352  * @class Roo.menu.ColorItem
40353  * @extends Roo.menu.Adapter
40354  * A menu item that wraps the {@link Roo.ColorPalette} component.
40355  * @constructor
40356  * Creates a new ColorItem
40357  * @param {Object} config Configuration options
40358  */
40359 Roo.menu.ColorItem = function(config){
40360     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40361     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40362     this.palette = this.component;
40363     this.relayEvents(this.palette, ["select"]);
40364     if(this.selectHandler){
40365         this.on('select', this.selectHandler, this.scope);
40366     }
40367 };
40368 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40369  * Based on:
40370  * Ext JS Library 1.1.1
40371  * Copyright(c) 2006-2007, Ext JS, LLC.
40372  *
40373  * Originally Released Under LGPL - original licence link has changed is not relivant.
40374  *
40375  * Fork - LGPL
40376  * <script type="text/javascript">
40377  */
40378  
40379
40380 /**
40381  * @class Roo.menu.DateMenu
40382  * @extends Roo.menu.Menu
40383  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40384  * @constructor
40385  * Creates a new DateMenu
40386  * @param {Object} config Configuration options
40387  */
40388 Roo.menu.DateMenu = function(config){
40389     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40390     this.plain = true;
40391     var di = new Roo.menu.DateItem(config);
40392     this.add(di);
40393     /**
40394      * The {@link Roo.DatePicker} instance for this DateMenu
40395      * @type DatePicker
40396      */
40397     this.picker = di.picker;
40398     /**
40399      * @event select
40400      * @param {DatePicker} picker
40401      * @param {Date} date
40402      */
40403     this.relayEvents(di, ["select"]);
40404     this.on('beforeshow', function(){
40405         if(this.picker){
40406             this.picker.hideMonthPicker(false);
40407         }
40408     }, this);
40409 };
40410 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40411     cls:'x-date-menu'
40412 });/*
40413  * Based on:
40414  * Ext JS Library 1.1.1
40415  * Copyright(c) 2006-2007, Ext JS, LLC.
40416  *
40417  * Originally Released Under LGPL - original licence link has changed is not relivant.
40418  *
40419  * Fork - LGPL
40420  * <script type="text/javascript">
40421  */
40422  
40423
40424 /**
40425  * @class Roo.menu.ColorMenu
40426  * @extends Roo.menu.Menu
40427  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40428  * @constructor
40429  * Creates a new ColorMenu
40430  * @param {Object} config Configuration options
40431  */
40432 Roo.menu.ColorMenu = function(config){
40433     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40434     this.plain = true;
40435     var ci = new Roo.menu.ColorItem(config);
40436     this.add(ci);
40437     /**
40438      * The {@link Roo.ColorPalette} instance for this ColorMenu
40439      * @type ColorPalette
40440      */
40441     this.palette = ci.palette;
40442     /**
40443      * @event select
40444      * @param {ColorPalette} palette
40445      * @param {String} color
40446      */
40447     this.relayEvents(ci, ["select"]);
40448 };
40449 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40450  * Based on:
40451  * Ext JS Library 1.1.1
40452  * Copyright(c) 2006-2007, Ext JS, LLC.
40453  *
40454  * Originally Released Under LGPL - original licence link has changed is not relivant.
40455  *
40456  * Fork - LGPL
40457  * <script type="text/javascript">
40458  */
40459  
40460 /**
40461  * @class Roo.form.TextItem
40462  * @extends Roo.BoxComponent
40463  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40464  * @constructor
40465  * Creates a new TextItem
40466  * @param {Object} config Configuration options
40467  */
40468 Roo.form.TextItem = function(config){
40469     Roo.form.TextItem.superclass.constructor.call(this, config);
40470 };
40471
40472 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40473     
40474     /**
40475      * @cfg {String} tag the tag for this item (default div)
40476      */
40477     tag : 'div',
40478     /**
40479      * @cfg {String} html the content for this item
40480      */
40481     html : '',
40482     
40483     getAutoCreate : function()
40484     {
40485         var cfg = {
40486             id: this.id,
40487             tag: this.tag,
40488             html: this.html,
40489             cls: 'x-form-item'
40490         };
40491         
40492         return cfg;
40493         
40494     },
40495     
40496     onRender : function(ct, position)
40497     {
40498         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40499         
40500         if(!this.el){
40501             var cfg = this.getAutoCreate();
40502             if(!cfg.name){
40503                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40504             }
40505             if (!cfg.name.length) {
40506                 delete cfg.name;
40507             }
40508             this.el = ct.createChild(cfg, position);
40509         }
40510     },
40511     /*
40512      * setHTML
40513      * @param {String} html update the Contents of the element.
40514      */
40515     setHTML : function(html)
40516     {
40517         this.fieldEl.dom.innerHTML = html;
40518     }
40519     
40520 });/*
40521  * Based on:
40522  * Ext JS Library 1.1.1
40523  * Copyright(c) 2006-2007, Ext JS, LLC.
40524  *
40525  * Originally Released Under LGPL - original licence link has changed is not relivant.
40526  *
40527  * Fork - LGPL
40528  * <script type="text/javascript">
40529  */
40530  
40531 /**
40532  * @class Roo.form.Field
40533  * @extends Roo.BoxComponent
40534  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40535  * @constructor
40536  * Creates a new Field
40537  * @param {Object} config Configuration options
40538  */
40539 Roo.form.Field = function(config){
40540     Roo.form.Field.superclass.constructor.call(this, config);
40541 };
40542
40543 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40544     /**
40545      * @cfg {String} fieldLabel Label to use when rendering a form.
40546      */
40547        /**
40548      * @cfg {String} qtip Mouse over tip
40549      */
40550      
40551     /**
40552      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40553      */
40554     invalidClass : "x-form-invalid",
40555     /**
40556      * @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")
40557      */
40558     invalidText : "The value in this field is invalid",
40559     /**
40560      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40561      */
40562     focusClass : "x-form-focus",
40563     /**
40564      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40565       automatic validation (defaults to "keyup").
40566      */
40567     validationEvent : "keyup",
40568     /**
40569      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40570      */
40571     validateOnBlur : true,
40572     /**
40573      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40574      */
40575     validationDelay : 250,
40576     /**
40577      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40578      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40579      */
40580     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40581     /**
40582      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40583      */
40584     fieldClass : "x-form-field",
40585     /**
40586      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40587      *<pre>
40588 Value         Description
40589 -----------   ----------------------------------------------------------------------
40590 qtip          Display a quick tip when the user hovers over the field
40591 title         Display a default browser title attribute popup
40592 under         Add a block div beneath the field containing the error text
40593 side          Add an error icon to the right of the field with a popup on hover
40594 [element id]  Add the error text directly to the innerHTML of the specified element
40595 </pre>
40596      */
40597     msgTarget : 'qtip',
40598     /**
40599      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40600      */
40601     msgFx : 'normal',
40602
40603     /**
40604      * @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.
40605      */
40606     readOnly : false,
40607
40608     /**
40609      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40610      */
40611     disabled : false,
40612
40613     /**
40614      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40615      */
40616     inputType : undefined,
40617     
40618     /**
40619      * @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).
40620          */
40621         tabIndex : undefined,
40622         
40623     // private
40624     isFormField : true,
40625
40626     // private
40627     hasFocus : false,
40628     /**
40629      * @property {Roo.Element} fieldEl
40630      * Element Containing the rendered Field (with label etc.)
40631      */
40632     /**
40633      * @cfg {Mixed} value A value to initialize this field with.
40634      */
40635     value : undefined,
40636
40637     /**
40638      * @cfg {String} name The field's HTML name attribute.
40639      */
40640     /**
40641      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40642      */
40643     // private
40644     loadedValue : false,
40645      
40646      
40647         // private ??
40648         initComponent : function(){
40649         Roo.form.Field.superclass.initComponent.call(this);
40650         this.addEvents({
40651             /**
40652              * @event focus
40653              * Fires when this field receives input focus.
40654              * @param {Roo.form.Field} this
40655              */
40656             focus : true,
40657             /**
40658              * @event blur
40659              * Fires when this field loses input focus.
40660              * @param {Roo.form.Field} this
40661              */
40662             blur : true,
40663             /**
40664              * @event specialkey
40665              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40666              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40667              * @param {Roo.form.Field} this
40668              * @param {Roo.EventObject} e The event object
40669              */
40670             specialkey : true,
40671             /**
40672              * @event change
40673              * Fires just before the field blurs if the field value has changed.
40674              * @param {Roo.form.Field} this
40675              * @param {Mixed} newValue The new value
40676              * @param {Mixed} oldValue The original value
40677              */
40678             change : true,
40679             /**
40680              * @event invalid
40681              * Fires after the field has been marked as invalid.
40682              * @param {Roo.form.Field} this
40683              * @param {String} msg The validation message
40684              */
40685             invalid : true,
40686             /**
40687              * @event valid
40688              * Fires after the field has been validated with no errors.
40689              * @param {Roo.form.Field} this
40690              */
40691             valid : true,
40692              /**
40693              * @event keyup
40694              * Fires after the key up
40695              * @param {Roo.form.Field} this
40696              * @param {Roo.EventObject}  e The event Object
40697              */
40698             keyup : true
40699         });
40700     },
40701
40702     /**
40703      * Returns the name attribute of the field if available
40704      * @return {String} name The field name
40705      */
40706     getName: function(){
40707          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40708     },
40709
40710     // private
40711     onRender : function(ct, position){
40712         Roo.form.Field.superclass.onRender.call(this, ct, position);
40713         if(!this.el){
40714             var cfg = this.getAutoCreate();
40715             if(!cfg.name){
40716                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40717             }
40718             if (!cfg.name.length) {
40719                 delete cfg.name;
40720             }
40721             if(this.inputType){
40722                 cfg.type = this.inputType;
40723             }
40724             this.el = ct.createChild(cfg, position);
40725         }
40726         var type = this.el.dom.type;
40727         if(type){
40728             if(type == 'password'){
40729                 type = 'text';
40730             }
40731             this.el.addClass('x-form-'+type);
40732         }
40733         if(this.readOnly){
40734             this.el.dom.readOnly = true;
40735         }
40736         if(this.tabIndex !== undefined){
40737             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40738         }
40739
40740         this.el.addClass([this.fieldClass, this.cls]);
40741         this.initValue();
40742     },
40743
40744     /**
40745      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40746      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40747      * @return {Roo.form.Field} this
40748      */
40749     applyTo : function(target){
40750         this.allowDomMove = false;
40751         this.el = Roo.get(target);
40752         this.render(this.el.dom.parentNode);
40753         return this;
40754     },
40755
40756     // private
40757     initValue : function(){
40758         if(this.value !== undefined){
40759             this.setValue(this.value);
40760         }else if(this.el.dom.value.length > 0){
40761             this.setValue(this.el.dom.value);
40762         }
40763     },
40764
40765     /**
40766      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40767      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40768      */
40769     isDirty : function() {
40770         if(this.disabled) {
40771             return false;
40772         }
40773         return String(this.getValue()) !== String(this.originalValue);
40774     },
40775
40776     /**
40777      * stores the current value in loadedValue
40778      */
40779     resetHasChanged : function()
40780     {
40781         this.loadedValue = String(this.getValue());
40782     },
40783     /**
40784      * checks the current value against the 'loaded' value.
40785      * Note - will return false if 'resetHasChanged' has not been called first.
40786      */
40787     hasChanged : function()
40788     {
40789         if(this.disabled || this.readOnly) {
40790             return false;
40791         }
40792         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40793     },
40794     
40795     
40796     
40797     // private
40798     afterRender : function(){
40799         Roo.form.Field.superclass.afterRender.call(this);
40800         this.initEvents();
40801     },
40802
40803     // private
40804     fireKey : function(e){
40805         //Roo.log('field ' + e.getKey());
40806         if(e.isNavKeyPress()){
40807             this.fireEvent("specialkey", this, e);
40808         }
40809     },
40810
40811     /**
40812      * Resets the current field value to the originally loaded value and clears any validation messages
40813      */
40814     reset : function(){
40815         this.setValue(this.resetValue);
40816         this.originalValue = this.getValue();
40817         this.clearInvalid();
40818     },
40819
40820     // private
40821     initEvents : function(){
40822         // safari killled keypress - so keydown is now used..
40823         this.el.on("keydown" , this.fireKey,  this);
40824         this.el.on("focus", this.onFocus,  this);
40825         this.el.on("blur", this.onBlur,  this);
40826         this.el.relayEvent('keyup', this);
40827
40828         // reference to original value for reset
40829         this.originalValue = this.getValue();
40830         this.resetValue =  this.getValue();
40831     },
40832
40833     // private
40834     onFocus : function(){
40835         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40836             this.el.addClass(this.focusClass);
40837         }
40838         if(!this.hasFocus){
40839             this.hasFocus = true;
40840             this.startValue = this.getValue();
40841             this.fireEvent("focus", this);
40842         }
40843     },
40844
40845     beforeBlur : Roo.emptyFn,
40846
40847     // private
40848     onBlur : function(){
40849         this.beforeBlur();
40850         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40851             this.el.removeClass(this.focusClass);
40852         }
40853         this.hasFocus = false;
40854         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40855             this.validate();
40856         }
40857         var v = this.getValue();
40858         if(String(v) !== String(this.startValue)){
40859             this.fireEvent('change', this, v, this.startValue);
40860         }
40861         this.fireEvent("blur", this);
40862     },
40863
40864     /**
40865      * Returns whether or not the field value is currently valid
40866      * @param {Boolean} preventMark True to disable marking the field invalid
40867      * @return {Boolean} True if the value is valid, else false
40868      */
40869     isValid : function(preventMark){
40870         if(this.disabled){
40871             return true;
40872         }
40873         var restore = this.preventMark;
40874         this.preventMark = preventMark === true;
40875         var v = this.validateValue(this.processValue(this.getRawValue()));
40876         this.preventMark = restore;
40877         return v;
40878     },
40879
40880     /**
40881      * Validates the field value
40882      * @return {Boolean} True if the value is valid, else false
40883      */
40884     validate : function(){
40885         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40886             this.clearInvalid();
40887             return true;
40888         }
40889         return false;
40890     },
40891
40892     processValue : function(value){
40893         return value;
40894     },
40895
40896     // private
40897     // Subclasses should provide the validation implementation by overriding this
40898     validateValue : function(value){
40899         return true;
40900     },
40901
40902     /**
40903      * Mark this field as invalid
40904      * @param {String} msg The validation message
40905      */
40906     markInvalid : function(msg){
40907         if(!this.rendered || this.preventMark){ // not rendered
40908             return;
40909         }
40910         
40911         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40912         
40913         obj.el.addClass(this.invalidClass);
40914         msg = msg || this.invalidText;
40915         switch(this.msgTarget){
40916             case 'qtip':
40917                 obj.el.dom.qtip = msg;
40918                 obj.el.dom.qclass = 'x-form-invalid-tip';
40919                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40920                     Roo.QuickTips.enable();
40921                 }
40922                 break;
40923             case 'title':
40924                 this.el.dom.title = msg;
40925                 break;
40926             case 'under':
40927                 if(!this.errorEl){
40928                     var elp = this.el.findParent('.x-form-element', 5, true);
40929                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40930                     this.errorEl.setWidth(elp.getWidth(true)-20);
40931                 }
40932                 this.errorEl.update(msg);
40933                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40934                 break;
40935             case 'side':
40936                 if(!this.errorIcon){
40937                     var elp = this.el.findParent('.x-form-element', 5, true);
40938                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40939                 }
40940                 this.alignErrorIcon();
40941                 this.errorIcon.dom.qtip = msg;
40942                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40943                 this.errorIcon.show();
40944                 this.on('resize', this.alignErrorIcon, this);
40945                 break;
40946             default:
40947                 var t = Roo.getDom(this.msgTarget);
40948                 t.innerHTML = msg;
40949                 t.style.display = this.msgDisplay;
40950                 break;
40951         }
40952         this.fireEvent('invalid', this, msg);
40953     },
40954
40955     // private
40956     alignErrorIcon : function(){
40957         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40958     },
40959
40960     /**
40961      * Clear any invalid styles/messages for this field
40962      */
40963     clearInvalid : function(){
40964         if(!this.rendered || this.preventMark){ // not rendered
40965             return;
40966         }
40967         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40968         
40969         obj.el.removeClass(this.invalidClass);
40970         switch(this.msgTarget){
40971             case 'qtip':
40972                 obj.el.dom.qtip = '';
40973                 break;
40974             case 'title':
40975                 this.el.dom.title = '';
40976                 break;
40977             case 'under':
40978                 if(this.errorEl){
40979                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40980                 }
40981                 break;
40982             case 'side':
40983                 if(this.errorIcon){
40984                     this.errorIcon.dom.qtip = '';
40985                     this.errorIcon.hide();
40986                     this.un('resize', this.alignErrorIcon, this);
40987                 }
40988                 break;
40989             default:
40990                 var t = Roo.getDom(this.msgTarget);
40991                 t.innerHTML = '';
40992                 t.style.display = 'none';
40993                 break;
40994         }
40995         this.fireEvent('valid', this);
40996     },
40997
40998     /**
40999      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41000      * @return {Mixed} value The field value
41001      */
41002     getRawValue : function(){
41003         var v = this.el.getValue();
41004         
41005         return v;
41006     },
41007
41008     /**
41009      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41010      * @return {Mixed} value The field value
41011      */
41012     getValue : function(){
41013         var v = this.el.getValue();
41014          
41015         return v;
41016     },
41017
41018     /**
41019      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
41020      * @param {Mixed} value The value to set
41021      */
41022     setRawValue : function(v){
41023         return this.el.dom.value = (v === null || v === undefined ? '' : v);
41024     },
41025
41026     /**
41027      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41028      * @param {Mixed} value The value to set
41029      */
41030     setValue : function(v){
41031         this.value = v;
41032         if(this.rendered){
41033             this.el.dom.value = (v === null || v === undefined ? '' : v);
41034              this.validate();
41035         }
41036     },
41037
41038     adjustSize : function(w, h){
41039         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41040         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41041         return s;
41042     },
41043
41044     adjustWidth : function(tag, w){
41045         tag = tag.toLowerCase();
41046         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41047             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41048                 if(tag == 'input'){
41049                     return w + 2;
41050                 }
41051                 if(tag == 'textarea'){
41052                     return w-2;
41053                 }
41054             }else if(Roo.isOpera){
41055                 if(tag == 'input'){
41056                     return w + 2;
41057                 }
41058                 if(tag == 'textarea'){
41059                     return w-2;
41060                 }
41061             }
41062         }
41063         return w;
41064     }
41065 });
41066
41067
41068 // anything other than normal should be considered experimental
41069 Roo.form.Field.msgFx = {
41070     normal : {
41071         show: function(msgEl, f){
41072             msgEl.setDisplayed('block');
41073         },
41074
41075         hide : function(msgEl, f){
41076             msgEl.setDisplayed(false).update('');
41077         }
41078     },
41079
41080     slide : {
41081         show: function(msgEl, f){
41082             msgEl.slideIn('t', {stopFx:true});
41083         },
41084
41085         hide : function(msgEl, f){
41086             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41087         }
41088     },
41089
41090     slideRight : {
41091         show: function(msgEl, f){
41092             msgEl.fixDisplay();
41093             msgEl.alignTo(f.el, 'tl-tr');
41094             msgEl.slideIn('l', {stopFx:true});
41095         },
41096
41097         hide : function(msgEl, f){
41098             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41099         }
41100     }
41101 };/*
41102  * Based on:
41103  * Ext JS Library 1.1.1
41104  * Copyright(c) 2006-2007, Ext JS, LLC.
41105  *
41106  * Originally Released Under LGPL - original licence link has changed is not relivant.
41107  *
41108  * Fork - LGPL
41109  * <script type="text/javascript">
41110  */
41111  
41112
41113 /**
41114  * @class Roo.form.TextField
41115  * @extends Roo.form.Field
41116  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
41117  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41118  * @constructor
41119  * Creates a new TextField
41120  * @param {Object} config Configuration options
41121  */
41122 Roo.form.TextField = function(config){
41123     Roo.form.TextField.superclass.constructor.call(this, config);
41124     this.addEvents({
41125         /**
41126          * @event autosize
41127          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
41128          * according to the default logic, but this event provides a hook for the developer to apply additional
41129          * logic at runtime to resize the field if needed.
41130              * @param {Roo.form.Field} this This text field
41131              * @param {Number} width The new field width
41132              */
41133         autosize : true
41134     });
41135 };
41136
41137 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
41138     /**
41139      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41140      */
41141     grow : false,
41142     /**
41143      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41144      */
41145     growMin : 30,
41146     /**
41147      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41148      */
41149     growMax : 800,
41150     /**
41151      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41152      */
41153     vtype : null,
41154     /**
41155      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41156      */
41157     maskRe : null,
41158     /**
41159      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41160      */
41161     disableKeyFilter : false,
41162     /**
41163      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41164      */
41165     allowBlank : true,
41166     /**
41167      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41168      */
41169     minLength : 0,
41170     /**
41171      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41172      */
41173     maxLength : Number.MAX_VALUE,
41174     /**
41175      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41176      */
41177     minLengthText : "The minimum length for this field is {0}",
41178     /**
41179      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41180      */
41181     maxLengthText : "The maximum length for this field is {0}",
41182     /**
41183      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41184      */
41185     selectOnFocus : false,
41186     /**
41187      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
41188      */    
41189     allowLeadingSpace : false,
41190     /**
41191      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41192      */
41193     blankText : "This field is required",
41194     /**
41195      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41196      * If available, this function will be called only after the basic validators all return true, and will be passed the
41197      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41198      */
41199     validator : null,
41200     /**
41201      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41202      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41203      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
41204      */
41205     regex : null,
41206     /**
41207      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41208      */
41209     regexText : "",
41210     /**
41211      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41212      */
41213     emptyText : null,
41214    
41215
41216     // private
41217     initEvents : function()
41218     {
41219         if (this.emptyText) {
41220             this.el.attr('placeholder', this.emptyText);
41221         }
41222         
41223         Roo.form.TextField.superclass.initEvents.call(this);
41224         if(this.validationEvent == 'keyup'){
41225             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41226             this.el.on('keyup', this.filterValidation, this);
41227         }
41228         else if(this.validationEvent !== false){
41229             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41230         }
41231         
41232         if(this.selectOnFocus){
41233             this.on("focus", this.preFocus, this);
41234         }
41235         if (!this.allowLeadingSpace) {
41236             this.on('blur', this.cleanLeadingSpace, this);
41237         }
41238         
41239         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41240             this.el.on("keypress", this.filterKeys, this);
41241         }
41242         if(this.grow){
41243             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41244             this.el.on("click", this.autoSize,  this);
41245         }
41246         if(this.el.is('input[type=password]') && Roo.isSafari){
41247             this.el.on('keydown', this.SafariOnKeyDown, this);
41248         }
41249     },
41250
41251     processValue : function(value){
41252         if(this.stripCharsRe){
41253             var newValue = value.replace(this.stripCharsRe, '');
41254             if(newValue !== value){
41255                 this.setRawValue(newValue);
41256                 return newValue;
41257             }
41258         }
41259         return value;
41260     },
41261
41262     filterValidation : function(e){
41263         if(!e.isNavKeyPress()){
41264             this.validationTask.delay(this.validationDelay);
41265         }
41266     },
41267
41268     // private
41269     onKeyUp : function(e){
41270         if(!e.isNavKeyPress()){
41271             this.autoSize();
41272         }
41273     },
41274     // private - clean the leading white space
41275     cleanLeadingSpace : function(e)
41276     {
41277         if ( this.inputType == 'file') {
41278             return;
41279         }
41280         
41281         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41282     },
41283     /**
41284      * Resets the current field value to the originally-loaded value and clears any validation messages.
41285      *  
41286      */
41287     reset : function(){
41288         Roo.form.TextField.superclass.reset.call(this);
41289        
41290     }, 
41291     // private
41292     preFocus : function(){
41293         
41294         if(this.selectOnFocus){
41295             this.el.dom.select();
41296         }
41297     },
41298
41299     
41300     // private
41301     filterKeys : function(e){
41302         var k = e.getKey();
41303         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41304             return;
41305         }
41306         var c = e.getCharCode(), cc = String.fromCharCode(c);
41307         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41308             return;
41309         }
41310         if(!this.maskRe.test(cc)){
41311             e.stopEvent();
41312         }
41313     },
41314
41315     setValue : function(v){
41316         
41317         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41318         
41319         this.autoSize();
41320     },
41321
41322     /**
41323      * Validates a value according to the field's validation rules and marks the field as invalid
41324      * if the validation fails
41325      * @param {Mixed} value The value to validate
41326      * @return {Boolean} True if the value is valid, else false
41327      */
41328     validateValue : function(value){
41329         if(value.length < 1)  { // if it's blank
41330              if(this.allowBlank){
41331                 this.clearInvalid();
41332                 return true;
41333              }else{
41334                 this.markInvalid(this.blankText);
41335                 return false;
41336              }
41337         }
41338         if(value.length < this.minLength){
41339             this.markInvalid(String.format(this.minLengthText, this.minLength));
41340             return false;
41341         }
41342         if(value.length > this.maxLength){
41343             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41344             return false;
41345         }
41346         if(this.vtype){
41347             var vt = Roo.form.VTypes;
41348             if(!vt[this.vtype](value, this)){
41349                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41350                 return false;
41351             }
41352         }
41353         if(typeof this.validator == "function"){
41354             var msg = this.validator(value);
41355             if(msg !== true){
41356                 this.markInvalid(msg);
41357                 return false;
41358             }
41359         }
41360         if(this.regex && !this.regex.test(value)){
41361             this.markInvalid(this.regexText);
41362             return false;
41363         }
41364         return true;
41365     },
41366
41367     /**
41368      * Selects text in this field
41369      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41370      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41371      */
41372     selectText : function(start, end){
41373         var v = this.getRawValue();
41374         if(v.length > 0){
41375             start = start === undefined ? 0 : start;
41376             end = end === undefined ? v.length : end;
41377             var d = this.el.dom;
41378             if(d.setSelectionRange){
41379                 d.setSelectionRange(start, end);
41380             }else if(d.createTextRange){
41381                 var range = d.createTextRange();
41382                 range.moveStart("character", start);
41383                 range.moveEnd("character", v.length-end);
41384                 range.select();
41385             }
41386         }
41387     },
41388
41389     /**
41390      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41391      * This only takes effect if grow = true, and fires the autosize event.
41392      */
41393     autoSize : function(){
41394         if(!this.grow || !this.rendered){
41395             return;
41396         }
41397         if(!this.metrics){
41398             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41399         }
41400         var el = this.el;
41401         var v = el.dom.value;
41402         var d = document.createElement('div');
41403         d.appendChild(document.createTextNode(v));
41404         v = d.innerHTML;
41405         d = null;
41406         v += "&#160;";
41407         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41408         this.el.setWidth(w);
41409         this.fireEvent("autosize", this, w);
41410     },
41411     
41412     // private
41413     SafariOnKeyDown : function(event)
41414     {
41415         // this is a workaround for a password hang bug on chrome/ webkit.
41416         
41417         var isSelectAll = false;
41418         
41419         if(this.el.dom.selectionEnd > 0){
41420             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41421         }
41422         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41423             event.preventDefault();
41424             this.setValue('');
41425             return;
41426         }
41427         
41428         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41429             
41430             event.preventDefault();
41431             // this is very hacky as keydown always get's upper case.
41432             
41433             var cc = String.fromCharCode(event.getCharCode());
41434             
41435             
41436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41437             
41438         }
41439         
41440         
41441     }
41442 });/*
41443  * Based on:
41444  * Ext JS Library 1.1.1
41445  * Copyright(c) 2006-2007, Ext JS, LLC.
41446  *
41447  * Originally Released Under LGPL - original licence link has changed is not relivant.
41448  *
41449  * Fork - LGPL
41450  * <script type="text/javascript">
41451  */
41452  
41453 /**
41454  * @class Roo.form.Hidden
41455  * @extends Roo.form.TextField
41456  * Simple Hidden element used on forms 
41457  * 
41458  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41459  * 
41460  * @constructor
41461  * Creates a new Hidden form element.
41462  * @param {Object} config Configuration options
41463  */
41464
41465
41466
41467 // easy hidden field...
41468 Roo.form.Hidden = function(config){
41469     Roo.form.Hidden.superclass.constructor.call(this, config);
41470 };
41471   
41472 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41473     fieldLabel:      '',
41474     inputType:      'hidden',
41475     width:          50,
41476     allowBlank:     true,
41477     labelSeparator: '',
41478     hidden:         true,
41479     itemCls :       'x-form-item-display-none'
41480
41481
41482 });
41483
41484
41485 /*
41486  * Based on:
41487  * Ext JS Library 1.1.1
41488  * Copyright(c) 2006-2007, Ext JS, LLC.
41489  *
41490  * Originally Released Under LGPL - original licence link has changed is not relivant.
41491  *
41492  * Fork - LGPL
41493  * <script type="text/javascript">
41494  */
41495  
41496 /**
41497  * @class Roo.form.TriggerField
41498  * @extends Roo.form.TextField
41499  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41500  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41501  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41502  * for which you can provide a custom implementation.  For example:
41503  * <pre><code>
41504 var trigger = new Roo.form.TriggerField();
41505 trigger.onTriggerClick = myTriggerFn;
41506 trigger.applyTo('my-field');
41507 </code></pre>
41508  *
41509  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41510  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41511  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41512  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41513  * @constructor
41514  * Create a new TriggerField.
41515  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41516  * to the base TextField)
41517  */
41518 Roo.form.TriggerField = function(config){
41519     this.mimicing = false;
41520     Roo.form.TriggerField.superclass.constructor.call(this, config);
41521 };
41522
41523 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41524     /**
41525      * @cfg {String} triggerClass A CSS class to apply to the trigger
41526      */
41527     /**
41528      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41529      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41530      */
41531     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41532     /**
41533      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41534      */
41535     hideTrigger:false,
41536
41537     /** @cfg {Boolean} grow @hide */
41538     /** @cfg {Number} growMin @hide */
41539     /** @cfg {Number} growMax @hide */
41540
41541     /**
41542      * @hide 
41543      * @method
41544      */
41545     autoSize: Roo.emptyFn,
41546     // private
41547     monitorTab : true,
41548     // private
41549     deferHeight : true,
41550
41551     
41552     actionMode : 'wrap',
41553     // private
41554     onResize : function(w, h){
41555         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41556         if(typeof w == 'number'){
41557             var x = w - this.trigger.getWidth();
41558             this.el.setWidth(this.adjustWidth('input', x));
41559             this.trigger.setStyle('left', x+'px');
41560         }
41561     },
41562
41563     // private
41564     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41565
41566     // private
41567     getResizeEl : function(){
41568         return this.wrap;
41569     },
41570
41571     // private
41572     getPositionEl : function(){
41573         return this.wrap;
41574     },
41575
41576     // private
41577     alignErrorIcon : function(){
41578         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41579     },
41580
41581     // private
41582     onRender : function(ct, position){
41583         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41584         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41585         this.trigger = this.wrap.createChild(this.triggerConfig ||
41586                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41587         if(this.hideTrigger){
41588             this.trigger.setDisplayed(false);
41589         }
41590         this.initTrigger();
41591         if(!this.width){
41592             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41593         }
41594     },
41595
41596     // private
41597     initTrigger : function(){
41598         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41599         this.trigger.addClassOnOver('x-form-trigger-over');
41600         this.trigger.addClassOnClick('x-form-trigger-click');
41601     },
41602
41603     // private
41604     onDestroy : function(){
41605         if(this.trigger){
41606             this.trigger.removeAllListeners();
41607             this.trigger.remove();
41608         }
41609         if(this.wrap){
41610             this.wrap.remove();
41611         }
41612         Roo.form.TriggerField.superclass.onDestroy.call(this);
41613     },
41614
41615     // private
41616     onFocus : function(){
41617         Roo.form.TriggerField.superclass.onFocus.call(this);
41618         if(!this.mimicing){
41619             this.wrap.addClass('x-trigger-wrap-focus');
41620             this.mimicing = true;
41621             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41622             if(this.monitorTab){
41623                 this.el.on("keydown", this.checkTab, this);
41624             }
41625         }
41626     },
41627
41628     // private
41629     checkTab : function(e){
41630         if(e.getKey() == e.TAB){
41631             this.triggerBlur();
41632         }
41633     },
41634
41635     // private
41636     onBlur : function(){
41637         // do nothing
41638     },
41639
41640     // private
41641     mimicBlur : function(e, t){
41642         if(!this.wrap.contains(t) && this.validateBlur()){
41643             this.triggerBlur();
41644         }
41645     },
41646
41647     // private
41648     triggerBlur : function(){
41649         this.mimicing = false;
41650         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41651         if(this.monitorTab){
41652             this.el.un("keydown", this.checkTab, this);
41653         }
41654         this.wrap.removeClass('x-trigger-wrap-focus');
41655         Roo.form.TriggerField.superclass.onBlur.call(this);
41656     },
41657
41658     // private
41659     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41660     validateBlur : function(e, t){
41661         return true;
41662     },
41663
41664     // private
41665     onDisable : function(){
41666         Roo.form.TriggerField.superclass.onDisable.call(this);
41667         if(this.wrap){
41668             this.wrap.addClass('x-item-disabled');
41669         }
41670     },
41671
41672     // private
41673     onEnable : function(){
41674         Roo.form.TriggerField.superclass.onEnable.call(this);
41675         if(this.wrap){
41676             this.wrap.removeClass('x-item-disabled');
41677         }
41678     },
41679
41680     // private
41681     onShow : function(){
41682         var ae = this.getActionEl();
41683         
41684         if(ae){
41685             ae.dom.style.display = '';
41686             ae.dom.style.visibility = 'visible';
41687         }
41688     },
41689
41690     // private
41691     
41692     onHide : function(){
41693         var ae = this.getActionEl();
41694         ae.dom.style.display = 'none';
41695     },
41696
41697     /**
41698      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41699      * by an implementing function.
41700      * @method
41701      * @param {EventObject} e
41702      */
41703     onTriggerClick : Roo.emptyFn
41704 });
41705
41706 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41707 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41708 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41709 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41710     initComponent : function(){
41711         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41712
41713         this.triggerConfig = {
41714             tag:'span', cls:'x-form-twin-triggers', cn:[
41715             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41716             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41717         ]};
41718     },
41719
41720     getTrigger : function(index){
41721         return this.triggers[index];
41722     },
41723
41724     initTrigger : function(){
41725         var ts = this.trigger.select('.x-form-trigger', true);
41726         this.wrap.setStyle('overflow', 'hidden');
41727         var triggerField = this;
41728         ts.each(function(t, all, index){
41729             t.hide = function(){
41730                 var w = triggerField.wrap.getWidth();
41731                 this.dom.style.display = 'none';
41732                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41733             };
41734             t.show = function(){
41735                 var w = triggerField.wrap.getWidth();
41736                 this.dom.style.display = '';
41737                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41738             };
41739             var triggerIndex = 'Trigger'+(index+1);
41740
41741             if(this['hide'+triggerIndex]){
41742                 t.dom.style.display = 'none';
41743             }
41744             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41745             t.addClassOnOver('x-form-trigger-over');
41746             t.addClassOnClick('x-form-trigger-click');
41747         }, this);
41748         this.triggers = ts.elements;
41749     },
41750
41751     onTrigger1Click : Roo.emptyFn,
41752     onTrigger2Click : Roo.emptyFn
41753 });/*
41754  * Based on:
41755  * Ext JS Library 1.1.1
41756  * Copyright(c) 2006-2007, Ext JS, LLC.
41757  *
41758  * Originally Released Under LGPL - original licence link has changed is not relivant.
41759  *
41760  * Fork - LGPL
41761  * <script type="text/javascript">
41762  */
41763  
41764 /**
41765  * @class Roo.form.TextArea
41766  * @extends Roo.form.TextField
41767  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41768  * support for auto-sizing.
41769  * @constructor
41770  * Creates a new TextArea
41771  * @param {Object} config Configuration options
41772  */
41773 Roo.form.TextArea = function(config){
41774     Roo.form.TextArea.superclass.constructor.call(this, config);
41775     // these are provided exchanges for backwards compat
41776     // minHeight/maxHeight were replaced by growMin/growMax to be
41777     // compatible with TextField growing config values
41778     if(this.minHeight !== undefined){
41779         this.growMin = this.minHeight;
41780     }
41781     if(this.maxHeight !== undefined){
41782         this.growMax = this.maxHeight;
41783     }
41784 };
41785
41786 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41787     /**
41788      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41789      */
41790     growMin : 60,
41791     /**
41792      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41793      */
41794     growMax: 1000,
41795     /**
41796      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41797      * in the field (equivalent to setting overflow: hidden, defaults to false)
41798      */
41799     preventScrollbars: false,
41800     /**
41801      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41802      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41803      */
41804
41805     // private
41806     onRender : function(ct, position){
41807         if(!this.el){
41808             this.defaultAutoCreate = {
41809                 tag: "textarea",
41810                 style:"width:300px;height:60px;",
41811                 autocomplete: "new-password"
41812             };
41813         }
41814         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41815         if(this.grow){
41816             this.textSizeEl = Roo.DomHelper.append(document.body, {
41817                 tag: "pre", cls: "x-form-grow-sizer"
41818             });
41819             if(this.preventScrollbars){
41820                 this.el.setStyle("overflow", "hidden");
41821             }
41822             this.el.setHeight(this.growMin);
41823         }
41824     },
41825
41826     onDestroy : function(){
41827         if(this.textSizeEl){
41828             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41829         }
41830         Roo.form.TextArea.superclass.onDestroy.call(this);
41831     },
41832
41833     // private
41834     onKeyUp : function(e){
41835         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41836             this.autoSize();
41837         }
41838     },
41839
41840     /**
41841      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41842      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41843      */
41844     autoSize : function(){
41845         if(!this.grow || !this.textSizeEl){
41846             return;
41847         }
41848         var el = this.el;
41849         var v = el.dom.value;
41850         var ts = this.textSizeEl;
41851
41852         ts.innerHTML = '';
41853         ts.appendChild(document.createTextNode(v));
41854         v = ts.innerHTML;
41855
41856         Roo.fly(ts).setWidth(this.el.getWidth());
41857         if(v.length < 1){
41858             v = "&#160;&#160;";
41859         }else{
41860             if(Roo.isIE){
41861                 v = v.replace(/\n/g, '<p>&#160;</p>');
41862             }
41863             v += "&#160;\n&#160;";
41864         }
41865         ts.innerHTML = v;
41866         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41867         if(h != this.lastHeight){
41868             this.lastHeight = h;
41869             this.el.setHeight(h);
41870             this.fireEvent("autosize", this, h);
41871         }
41872     }
41873 });/*
41874  * Based on:
41875  * Ext JS Library 1.1.1
41876  * Copyright(c) 2006-2007, Ext JS, LLC.
41877  *
41878  * Originally Released Under LGPL - original licence link has changed is not relivant.
41879  *
41880  * Fork - LGPL
41881  * <script type="text/javascript">
41882  */
41883  
41884
41885 /**
41886  * @class Roo.form.NumberField
41887  * @extends Roo.form.TextField
41888  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41889  * @constructor
41890  * Creates a new NumberField
41891  * @param {Object} config Configuration options
41892  */
41893 Roo.form.NumberField = function(config){
41894     Roo.form.NumberField.superclass.constructor.call(this, config);
41895 };
41896
41897 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41898     /**
41899      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41900      */
41901     fieldClass: "x-form-field x-form-num-field",
41902     /**
41903      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41904      */
41905     allowDecimals : true,
41906     /**
41907      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41908      */
41909     decimalSeparator : ".",
41910     /**
41911      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41912      */
41913     decimalPrecision : 2,
41914     /**
41915      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41916      */
41917     allowNegative : true,
41918     /**
41919      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41920      */
41921     minValue : Number.NEGATIVE_INFINITY,
41922     /**
41923      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41924      */
41925     maxValue : Number.MAX_VALUE,
41926     /**
41927      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41928      */
41929     minText : "The minimum value for this field is {0}",
41930     /**
41931      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41932      */
41933     maxText : "The maximum value for this field is {0}",
41934     /**
41935      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41936      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41937      */
41938     nanText : "{0} is not a valid number",
41939
41940     // private
41941     initEvents : function(){
41942         Roo.form.NumberField.superclass.initEvents.call(this);
41943         var allowed = "0123456789";
41944         if(this.allowDecimals){
41945             allowed += this.decimalSeparator;
41946         }
41947         if(this.allowNegative){
41948             allowed += "-";
41949         }
41950         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41951         var keyPress = function(e){
41952             var k = e.getKey();
41953             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41954                 return;
41955             }
41956             var c = e.getCharCode();
41957             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41958                 e.stopEvent();
41959             }
41960         };
41961         this.el.on("keypress", keyPress, this);
41962     },
41963
41964     // private
41965     validateValue : function(value){
41966         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41967             return false;
41968         }
41969         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41970              return true;
41971         }
41972         var num = this.parseValue(value);
41973         if(isNaN(num)){
41974             this.markInvalid(String.format(this.nanText, value));
41975             return false;
41976         }
41977         if(num < this.minValue){
41978             this.markInvalid(String.format(this.minText, this.minValue));
41979             return false;
41980         }
41981         if(num > this.maxValue){
41982             this.markInvalid(String.format(this.maxText, this.maxValue));
41983             return false;
41984         }
41985         return true;
41986     },
41987
41988     getValue : function(){
41989         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41990     },
41991
41992     // private
41993     parseValue : function(value){
41994         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41995         return isNaN(value) ? '' : value;
41996     },
41997
41998     // private
41999     fixPrecision : function(value){
42000         var nan = isNaN(value);
42001         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42002             return nan ? '' : value;
42003         }
42004         return parseFloat(value).toFixed(this.decimalPrecision);
42005     },
42006
42007     setValue : function(v){
42008         v = this.fixPrecision(v);
42009         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
42010     },
42011
42012     // private
42013     decimalPrecisionFcn : function(v){
42014         return Math.floor(v);
42015     },
42016
42017     beforeBlur : function(){
42018         var v = this.parseValue(this.getRawValue());
42019         if(v){
42020             this.setValue(v);
42021         }
42022     }
42023 });/*
42024  * Based on:
42025  * Ext JS Library 1.1.1
42026  * Copyright(c) 2006-2007, Ext JS, LLC.
42027  *
42028  * Originally Released Under LGPL - original licence link has changed is not relivant.
42029  *
42030  * Fork - LGPL
42031  * <script type="text/javascript">
42032  */
42033  
42034 /**
42035  * @class Roo.form.DateField
42036  * @extends Roo.form.TriggerField
42037  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42038 * @constructor
42039 * Create a new DateField
42040 * @param {Object} config
42041  */
42042 Roo.form.DateField = function(config)
42043 {
42044     Roo.form.DateField.superclass.constructor.call(this, config);
42045     
42046       this.addEvents({
42047          
42048         /**
42049          * @event select
42050          * Fires when a date is selected
42051              * @param {Roo.form.DateField} combo This combo box
42052              * @param {Date} date The date selected
42053              */
42054         'select' : true
42055          
42056     });
42057     
42058     
42059     if(typeof this.minValue == "string") {
42060         this.minValue = this.parseDate(this.minValue);
42061     }
42062     if(typeof this.maxValue == "string") {
42063         this.maxValue = this.parseDate(this.maxValue);
42064     }
42065     this.ddMatch = null;
42066     if(this.disabledDates){
42067         var dd = this.disabledDates;
42068         var re = "(?:";
42069         for(var i = 0; i < dd.length; i++){
42070             re += dd[i];
42071             if(i != dd.length-1) {
42072                 re += "|";
42073             }
42074         }
42075         this.ddMatch = new RegExp(re + ")");
42076     }
42077 };
42078
42079 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
42080     /**
42081      * @cfg {String} format
42082      * The default date format string which can be overriden for localization support.  The format must be
42083      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42084      */
42085     format : "m/d/y",
42086     /**
42087      * @cfg {String} altFormats
42088      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42089      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42090      */
42091     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42092     /**
42093      * @cfg {Array} disabledDays
42094      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42095      */
42096     disabledDays : null,
42097     /**
42098      * @cfg {String} disabledDaysText
42099      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42100      */
42101     disabledDaysText : "Disabled",
42102     /**
42103      * @cfg {Array} disabledDates
42104      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42105      * expression so they are very powerful. Some examples:
42106      * <ul>
42107      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42108      * <li>["03/08", "09/16"] would disable those days for every year</li>
42109      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42110      * <li>["03/../2006"] would disable every day in March 2006</li>
42111      * <li>["^03"] would disable every day in every March</li>
42112      * </ul>
42113      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42114      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42115      */
42116     disabledDates : null,
42117     /**
42118      * @cfg {String} disabledDatesText
42119      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42120      */
42121     disabledDatesText : "Disabled",
42122         
42123         
42124         /**
42125      * @cfg {Date/String} zeroValue
42126      * if the date is less that this number, then the field is rendered as empty
42127      * default is 1800
42128      */
42129         zeroValue : '1800-01-01',
42130         
42131         
42132     /**
42133      * @cfg {Date/String} minValue
42134      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42135      * valid format (defaults to null).
42136      */
42137     minValue : null,
42138     /**
42139      * @cfg {Date/String} maxValue
42140      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42141      * valid format (defaults to null).
42142      */
42143     maxValue : null,
42144     /**
42145      * @cfg {String} minText
42146      * The error text to display when the date in the cell is before minValue (defaults to
42147      * 'The date in this field must be after {minValue}').
42148      */
42149     minText : "The date in this field must be equal to or after {0}",
42150     /**
42151      * @cfg {String} maxText
42152      * The error text to display when the date in the cell is after maxValue (defaults to
42153      * 'The date in this field must be before {maxValue}').
42154      */
42155     maxText : "The date in this field must be equal to or before {0}",
42156     /**
42157      * @cfg {String} invalidText
42158      * The error text to display when the date in the field is invalid (defaults to
42159      * '{value} is not a valid date - it must be in the format {format}').
42160      */
42161     invalidText : "{0} is not a valid date - it must be in the format {1}",
42162     /**
42163      * @cfg {String} triggerClass
42164      * An additional CSS class used to style the trigger button.  The trigger will always get the
42165      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42166      * which displays a calendar icon).
42167      */
42168     triggerClass : 'x-form-date-trigger',
42169     
42170
42171     /**
42172      * @cfg {Boolean} useIso
42173      * if enabled, then the date field will use a hidden field to store the 
42174      * real value as iso formated date. default (false)
42175      */ 
42176     useIso : false,
42177     /**
42178      * @cfg {String/Object} autoCreate
42179      * A DomHelper element spec, or true for a default element spec (defaults to
42180      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42181      */ 
42182     // private
42183     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42184     
42185     // private
42186     hiddenField: false,
42187     
42188     onRender : function(ct, position)
42189     {
42190         Roo.form.DateField.superclass.onRender.call(this, ct, position);
42191         if (this.useIso) {
42192             //this.el.dom.removeAttribute('name'); 
42193             Roo.log("Changing name?");
42194             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
42195             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42196                     'before', true);
42197             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42198             // prevent input submission
42199             this.hiddenName = this.name;
42200         }
42201             
42202             
42203     },
42204     
42205     // private
42206     validateValue : function(value)
42207     {
42208         value = this.formatDate(value);
42209         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42210             Roo.log('super failed');
42211             return false;
42212         }
42213         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42214              return true;
42215         }
42216         var svalue = value;
42217         value = this.parseDate(value);
42218         if(!value){
42219             Roo.log('parse date failed' + svalue);
42220             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42221             return false;
42222         }
42223         var time = value.getTime();
42224         if(this.minValue && time < this.minValue.getTime()){
42225             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42226             return false;
42227         }
42228         if(this.maxValue && time > this.maxValue.getTime()){
42229             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42230             return false;
42231         }
42232         if(this.disabledDays){
42233             var day = value.getDay();
42234             for(var i = 0; i < this.disabledDays.length; i++) {
42235                 if(day === this.disabledDays[i]){
42236                     this.markInvalid(this.disabledDaysText);
42237                     return false;
42238                 }
42239             }
42240         }
42241         var fvalue = this.formatDate(value);
42242         if(this.ddMatch && this.ddMatch.test(fvalue)){
42243             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42244             return false;
42245         }
42246         return true;
42247     },
42248
42249     // private
42250     // Provides logic to override the default TriggerField.validateBlur which just returns true
42251     validateBlur : function(){
42252         return !this.menu || !this.menu.isVisible();
42253     },
42254     
42255     getName: function()
42256     {
42257         // returns hidden if it's set..
42258         if (!this.rendered) {return ''};
42259         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42260         
42261     },
42262
42263     /**
42264      * Returns the current date value of the date field.
42265      * @return {Date} The date value
42266      */
42267     getValue : function(){
42268         
42269         return  this.hiddenField ?
42270                 this.hiddenField.value :
42271                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42272     },
42273
42274     /**
42275      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42276      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42277      * (the default format used is "m/d/y").
42278      * <br />Usage:
42279      * <pre><code>
42280 //All of these calls set the same date value (May 4, 2006)
42281
42282 //Pass a date object:
42283 var dt = new Date('5/4/06');
42284 dateField.setValue(dt);
42285
42286 //Pass a date string (default format):
42287 dateField.setValue('5/4/06');
42288
42289 //Pass a date string (custom format):
42290 dateField.format = 'Y-m-d';
42291 dateField.setValue('2006-5-4');
42292 </code></pre>
42293      * @param {String/Date} date The date or valid date string
42294      */
42295     setValue : function(date){
42296         if (this.hiddenField) {
42297             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42298         }
42299         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42300         // make sure the value field is always stored as a date..
42301         this.value = this.parseDate(date);
42302         
42303         
42304     },
42305
42306     // private
42307     parseDate : function(value){
42308                 
42309                 if (value instanceof Date) {
42310                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42311                                 return  '';
42312                         }
42313                         return value;
42314                 }
42315                 
42316                 
42317         if(!value || value instanceof Date){
42318             return value;
42319         }
42320         var v = Date.parseDate(value, this.format);
42321          if (!v && this.useIso) {
42322             v = Date.parseDate(value, 'Y-m-d');
42323         }
42324         if(!v && this.altFormats){
42325             if(!this.altFormatsArray){
42326                 this.altFormatsArray = this.altFormats.split("|");
42327             }
42328             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42329                 v = Date.parseDate(value, this.altFormatsArray[i]);
42330             }
42331         }
42332                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42333                         v = '';
42334                 }
42335         return v;
42336     },
42337
42338     // private
42339     formatDate : function(date, fmt){
42340         return (!date || !(date instanceof Date)) ?
42341                date : date.dateFormat(fmt || this.format);
42342     },
42343
42344     // private
42345     menuListeners : {
42346         select: function(m, d){
42347             
42348             this.setValue(d);
42349             this.fireEvent('select', this, d);
42350         },
42351         show : function(){ // retain focus styling
42352             this.onFocus();
42353         },
42354         hide : function(){
42355             this.focus.defer(10, this);
42356             var ml = this.menuListeners;
42357             this.menu.un("select", ml.select,  this);
42358             this.menu.un("show", ml.show,  this);
42359             this.menu.un("hide", ml.hide,  this);
42360         }
42361     },
42362
42363     // private
42364     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42365     onTriggerClick : function(){
42366         if(this.disabled){
42367             return;
42368         }
42369         if(this.menu == null){
42370             this.menu = new Roo.menu.DateMenu();
42371         }
42372         Roo.apply(this.menu.picker,  {
42373             showClear: this.allowBlank,
42374             minDate : this.minValue,
42375             maxDate : this.maxValue,
42376             disabledDatesRE : this.ddMatch,
42377             disabledDatesText : this.disabledDatesText,
42378             disabledDays : this.disabledDays,
42379             disabledDaysText : this.disabledDaysText,
42380             format : this.useIso ? 'Y-m-d' : this.format,
42381             minText : String.format(this.minText, this.formatDate(this.minValue)),
42382             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42383         });
42384         this.menu.on(Roo.apply({}, this.menuListeners, {
42385             scope:this
42386         }));
42387         this.menu.picker.setValue(this.getValue() || new Date());
42388         this.menu.show(this.el, "tl-bl?");
42389     },
42390
42391     beforeBlur : function(){
42392         var v = this.parseDate(this.getRawValue());
42393         if(v){
42394             this.setValue(v);
42395         }
42396     },
42397
42398     /*@
42399      * overide
42400      * 
42401      */
42402     isDirty : function() {
42403         if(this.disabled) {
42404             return false;
42405         }
42406         
42407         if(typeof(this.startValue) === 'undefined'){
42408             return false;
42409         }
42410         
42411         return String(this.getValue()) !== String(this.startValue);
42412         
42413     },
42414     // @overide
42415     cleanLeadingSpace : function(e)
42416     {
42417        return;
42418     }
42419     
42420 });/*
42421  * Based on:
42422  * Ext JS Library 1.1.1
42423  * Copyright(c) 2006-2007, Ext JS, LLC.
42424  *
42425  * Originally Released Under LGPL - original licence link has changed is not relivant.
42426  *
42427  * Fork - LGPL
42428  * <script type="text/javascript">
42429  */
42430  
42431 /**
42432  * @class Roo.form.MonthField
42433  * @extends Roo.form.TriggerField
42434  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42435 * @constructor
42436 * Create a new MonthField
42437 * @param {Object} config
42438  */
42439 Roo.form.MonthField = function(config){
42440     
42441     Roo.form.MonthField.superclass.constructor.call(this, config);
42442     
42443       this.addEvents({
42444          
42445         /**
42446          * @event select
42447          * Fires when a date is selected
42448              * @param {Roo.form.MonthFieeld} combo This combo box
42449              * @param {Date} date The date selected
42450              */
42451         'select' : true
42452          
42453     });
42454     
42455     
42456     if(typeof this.minValue == "string") {
42457         this.minValue = this.parseDate(this.minValue);
42458     }
42459     if(typeof this.maxValue == "string") {
42460         this.maxValue = this.parseDate(this.maxValue);
42461     }
42462     this.ddMatch = null;
42463     if(this.disabledDates){
42464         var dd = this.disabledDates;
42465         var re = "(?:";
42466         for(var i = 0; i < dd.length; i++){
42467             re += dd[i];
42468             if(i != dd.length-1) {
42469                 re += "|";
42470             }
42471         }
42472         this.ddMatch = new RegExp(re + ")");
42473     }
42474 };
42475
42476 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42477     /**
42478      * @cfg {String} format
42479      * The default date format string which can be overriden for localization support.  The format must be
42480      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42481      */
42482     format : "M Y",
42483     /**
42484      * @cfg {String} altFormats
42485      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42486      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42487      */
42488     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42489     /**
42490      * @cfg {Array} disabledDays
42491      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42492      */
42493     disabledDays : [0,1,2,3,4,5,6],
42494     /**
42495      * @cfg {String} disabledDaysText
42496      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42497      */
42498     disabledDaysText : "Disabled",
42499     /**
42500      * @cfg {Array} disabledDates
42501      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42502      * expression so they are very powerful. Some examples:
42503      * <ul>
42504      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42505      * <li>["03/08", "09/16"] would disable those days for every year</li>
42506      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42507      * <li>["03/../2006"] would disable every day in March 2006</li>
42508      * <li>["^03"] would disable every day in every March</li>
42509      * </ul>
42510      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42511      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42512      */
42513     disabledDates : null,
42514     /**
42515      * @cfg {String} disabledDatesText
42516      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42517      */
42518     disabledDatesText : "Disabled",
42519     /**
42520      * @cfg {Date/String} minValue
42521      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42522      * valid format (defaults to null).
42523      */
42524     minValue : null,
42525     /**
42526      * @cfg {Date/String} maxValue
42527      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42528      * valid format (defaults to null).
42529      */
42530     maxValue : null,
42531     /**
42532      * @cfg {String} minText
42533      * The error text to display when the date in the cell is before minValue (defaults to
42534      * 'The date in this field must be after {minValue}').
42535      */
42536     minText : "The date in this field must be equal to or after {0}",
42537     /**
42538      * @cfg {String} maxTextf
42539      * The error text to display when the date in the cell is after maxValue (defaults to
42540      * 'The date in this field must be before {maxValue}').
42541      */
42542     maxText : "The date in this field must be equal to or before {0}",
42543     /**
42544      * @cfg {String} invalidText
42545      * The error text to display when the date in the field is invalid (defaults to
42546      * '{value} is not a valid date - it must be in the format {format}').
42547      */
42548     invalidText : "{0} is not a valid date - it must be in the format {1}",
42549     /**
42550      * @cfg {String} triggerClass
42551      * An additional CSS class used to style the trigger button.  The trigger will always get the
42552      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42553      * which displays a calendar icon).
42554      */
42555     triggerClass : 'x-form-date-trigger',
42556     
42557
42558     /**
42559      * @cfg {Boolean} useIso
42560      * if enabled, then the date field will use a hidden field to store the 
42561      * real value as iso formated date. default (true)
42562      */ 
42563     useIso : true,
42564     /**
42565      * @cfg {String/Object} autoCreate
42566      * A DomHelper element spec, or true for a default element spec (defaults to
42567      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42568      */ 
42569     // private
42570     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42571     
42572     // private
42573     hiddenField: false,
42574     
42575     hideMonthPicker : false,
42576     
42577     onRender : function(ct, position)
42578     {
42579         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42580         if (this.useIso) {
42581             this.el.dom.removeAttribute('name'); 
42582             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42583                     'before', true);
42584             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42585             // prevent input submission
42586             this.hiddenName = this.name;
42587         }
42588             
42589             
42590     },
42591     
42592     // private
42593     validateValue : function(value)
42594     {
42595         value = this.formatDate(value);
42596         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42597             return false;
42598         }
42599         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42600              return true;
42601         }
42602         var svalue = value;
42603         value = this.parseDate(value);
42604         if(!value){
42605             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42606             return false;
42607         }
42608         var time = value.getTime();
42609         if(this.minValue && time < this.minValue.getTime()){
42610             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42611             return false;
42612         }
42613         if(this.maxValue && time > this.maxValue.getTime()){
42614             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42615             return false;
42616         }
42617         /*if(this.disabledDays){
42618             var day = value.getDay();
42619             for(var i = 0; i < this.disabledDays.length; i++) {
42620                 if(day === this.disabledDays[i]){
42621                     this.markInvalid(this.disabledDaysText);
42622                     return false;
42623                 }
42624             }
42625         }
42626         */
42627         var fvalue = this.formatDate(value);
42628         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42629             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42630             return false;
42631         }
42632         */
42633         return true;
42634     },
42635
42636     // private
42637     // Provides logic to override the default TriggerField.validateBlur which just returns true
42638     validateBlur : function(){
42639         return !this.menu || !this.menu.isVisible();
42640     },
42641
42642     /**
42643      * Returns the current date value of the date field.
42644      * @return {Date} The date value
42645      */
42646     getValue : function(){
42647         
42648         
42649         
42650         return  this.hiddenField ?
42651                 this.hiddenField.value :
42652                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42653     },
42654
42655     /**
42656      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42657      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42658      * (the default format used is "m/d/y").
42659      * <br />Usage:
42660      * <pre><code>
42661 //All of these calls set the same date value (May 4, 2006)
42662
42663 //Pass a date object:
42664 var dt = new Date('5/4/06');
42665 monthField.setValue(dt);
42666
42667 //Pass a date string (default format):
42668 monthField.setValue('5/4/06');
42669
42670 //Pass a date string (custom format):
42671 monthField.format = 'Y-m-d';
42672 monthField.setValue('2006-5-4');
42673 </code></pre>
42674      * @param {String/Date} date The date or valid date string
42675      */
42676     setValue : function(date){
42677         Roo.log('month setValue' + date);
42678         // can only be first of month..
42679         
42680         var val = this.parseDate(date);
42681         
42682         if (this.hiddenField) {
42683             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42684         }
42685         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42686         this.value = this.parseDate(date);
42687     },
42688
42689     // private
42690     parseDate : function(value){
42691         if(!value || value instanceof Date){
42692             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42693             return value;
42694         }
42695         var v = Date.parseDate(value, this.format);
42696         if (!v && this.useIso) {
42697             v = Date.parseDate(value, 'Y-m-d');
42698         }
42699         if (v) {
42700             // 
42701             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42702         }
42703         
42704         
42705         if(!v && this.altFormats){
42706             if(!this.altFormatsArray){
42707                 this.altFormatsArray = this.altFormats.split("|");
42708             }
42709             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42710                 v = Date.parseDate(value, this.altFormatsArray[i]);
42711             }
42712         }
42713         return v;
42714     },
42715
42716     // private
42717     formatDate : function(date, fmt){
42718         return (!date || !(date instanceof Date)) ?
42719                date : date.dateFormat(fmt || this.format);
42720     },
42721
42722     // private
42723     menuListeners : {
42724         select: function(m, d){
42725             this.setValue(d);
42726             this.fireEvent('select', this, d);
42727         },
42728         show : function(){ // retain focus styling
42729             this.onFocus();
42730         },
42731         hide : function(){
42732             this.focus.defer(10, this);
42733             var ml = this.menuListeners;
42734             this.menu.un("select", ml.select,  this);
42735             this.menu.un("show", ml.show,  this);
42736             this.menu.un("hide", ml.hide,  this);
42737         }
42738     },
42739     // private
42740     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42741     onTriggerClick : function(){
42742         if(this.disabled){
42743             return;
42744         }
42745         if(this.menu == null){
42746             this.menu = new Roo.menu.DateMenu();
42747            
42748         }
42749         
42750         Roo.apply(this.menu.picker,  {
42751             
42752             showClear: this.allowBlank,
42753             minDate : this.minValue,
42754             maxDate : this.maxValue,
42755             disabledDatesRE : this.ddMatch,
42756             disabledDatesText : this.disabledDatesText,
42757             
42758             format : this.useIso ? 'Y-m-d' : this.format,
42759             minText : String.format(this.minText, this.formatDate(this.minValue)),
42760             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42761             
42762         });
42763          this.menu.on(Roo.apply({}, this.menuListeners, {
42764             scope:this
42765         }));
42766        
42767         
42768         var m = this.menu;
42769         var p = m.picker;
42770         
42771         // hide month picker get's called when we called by 'before hide';
42772         
42773         var ignorehide = true;
42774         p.hideMonthPicker  = function(disableAnim){
42775             if (ignorehide) {
42776                 return;
42777             }
42778              if(this.monthPicker){
42779                 Roo.log("hideMonthPicker called");
42780                 if(disableAnim === true){
42781                     this.monthPicker.hide();
42782                 }else{
42783                     this.monthPicker.slideOut('t', {duration:.2});
42784                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42785                     p.fireEvent("select", this, this.value);
42786                     m.hide();
42787                 }
42788             }
42789         }
42790         
42791         Roo.log('picker set value');
42792         Roo.log(this.getValue());
42793         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42794         m.show(this.el, 'tl-bl?');
42795         ignorehide  = false;
42796         // this will trigger hideMonthPicker..
42797         
42798         
42799         // hidden the day picker
42800         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42801         
42802         
42803         
42804       
42805         
42806         p.showMonthPicker.defer(100, p);
42807     
42808         
42809        
42810     },
42811
42812     beforeBlur : function(){
42813         var v = this.parseDate(this.getRawValue());
42814         if(v){
42815             this.setValue(v);
42816         }
42817     }
42818
42819     /** @cfg {Boolean} grow @hide */
42820     /** @cfg {Number} growMin @hide */
42821     /** @cfg {Number} growMax @hide */
42822     /**
42823      * @hide
42824      * @method autoSize
42825      */
42826 });/*
42827  * Based on:
42828  * Ext JS Library 1.1.1
42829  * Copyright(c) 2006-2007, Ext JS, LLC.
42830  *
42831  * Originally Released Under LGPL - original licence link has changed is not relivant.
42832  *
42833  * Fork - LGPL
42834  * <script type="text/javascript">
42835  */
42836  
42837
42838 /**
42839  * @class Roo.form.ComboBox
42840  * @extends Roo.form.TriggerField
42841  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42842  * @constructor
42843  * Create a new ComboBox.
42844  * @param {Object} config Configuration options
42845  */
42846 Roo.form.ComboBox = function(config){
42847     Roo.form.ComboBox.superclass.constructor.call(this, config);
42848     this.addEvents({
42849         /**
42850          * @event expand
42851          * Fires when the dropdown list is expanded
42852              * @param {Roo.form.ComboBox} combo This combo box
42853              */
42854         'expand' : true,
42855         /**
42856          * @event collapse
42857          * Fires when the dropdown list is collapsed
42858              * @param {Roo.form.ComboBox} combo This combo box
42859              */
42860         'collapse' : true,
42861         /**
42862          * @event beforeselect
42863          * Fires before a list item is selected. Return false to cancel the selection.
42864              * @param {Roo.form.ComboBox} combo This combo box
42865              * @param {Roo.data.Record} record The data record returned from the underlying store
42866              * @param {Number} index The index of the selected item in the dropdown list
42867              */
42868         'beforeselect' : true,
42869         /**
42870          * @event select
42871          * Fires when a list item is selected
42872              * @param {Roo.form.ComboBox} combo This combo box
42873              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42874              * @param {Number} index The index of the selected item in the dropdown list
42875              */
42876         'select' : true,
42877         /**
42878          * @event beforequery
42879          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42880          * The event object passed has these properties:
42881              * @param {Roo.form.ComboBox} combo This combo box
42882              * @param {String} query The query
42883              * @param {Boolean} forceAll true to force "all" query
42884              * @param {Boolean} cancel true to cancel the query
42885              * @param {Object} e The query event object
42886              */
42887         'beforequery': true,
42888          /**
42889          * @event add
42890          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42891              * @param {Roo.form.ComboBox} combo This combo box
42892              */
42893         'add' : true,
42894         /**
42895          * @event edit
42896          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42897              * @param {Roo.form.ComboBox} combo This combo box
42898              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42899              */
42900         'edit' : true
42901         
42902         
42903     });
42904     if(this.transform){
42905         this.allowDomMove = false;
42906         var s = Roo.getDom(this.transform);
42907         if(!this.hiddenName){
42908             this.hiddenName = s.name;
42909         }
42910         if(!this.store){
42911             this.mode = 'local';
42912             var d = [], opts = s.options;
42913             for(var i = 0, len = opts.length;i < len; i++){
42914                 var o = opts[i];
42915                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42916                 if(o.selected) {
42917                     this.value = value;
42918                 }
42919                 d.push([value, o.text]);
42920             }
42921             this.store = new Roo.data.SimpleStore({
42922                 'id': 0,
42923                 fields: ['value', 'text'],
42924                 data : d
42925             });
42926             this.valueField = 'value';
42927             this.displayField = 'text';
42928         }
42929         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42930         if(!this.lazyRender){
42931             this.target = true;
42932             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42933             s.parentNode.removeChild(s); // remove it
42934             this.render(this.el.parentNode);
42935         }else{
42936             s.parentNode.removeChild(s); // remove it
42937         }
42938
42939     }
42940     if (this.store) {
42941         this.store = Roo.factory(this.store, Roo.data);
42942     }
42943     
42944     this.selectedIndex = -1;
42945     if(this.mode == 'local'){
42946         if(config.queryDelay === undefined){
42947             this.queryDelay = 10;
42948         }
42949         if(config.minChars === undefined){
42950             this.minChars = 0;
42951         }
42952     }
42953 };
42954
42955 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42956     /**
42957      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42958      */
42959     /**
42960      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42961      * rendering into an Roo.Editor, defaults to false)
42962      */
42963     /**
42964      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42965      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42966      */
42967     /**
42968      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42969      */
42970     /**
42971      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42972      * the dropdown list (defaults to undefined, with no header element)
42973      */
42974
42975      /**
42976      * @cfg {String/Roo.Template} tpl The template to use to render the output
42977      */
42978      
42979     // private
42980     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42981     /**
42982      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42983      */
42984     listWidth: undefined,
42985     /**
42986      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42987      * mode = 'remote' or 'text' if mode = 'local')
42988      */
42989     displayField: undefined,
42990     /**
42991      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42992      * mode = 'remote' or 'value' if mode = 'local'). 
42993      * Note: use of a valueField requires the user make a selection
42994      * in order for a value to be mapped.
42995      */
42996     valueField: undefined,
42997     
42998     
42999     /**
43000      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
43001      * field's data value (defaults to the underlying DOM element's name)
43002      */
43003     hiddenName: undefined,
43004     /**
43005      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
43006      */
43007     listClass: '',
43008     /**
43009      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
43010      */
43011     selectedClass: 'x-combo-selected',
43012     /**
43013      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
43014      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
43015      * which displays a downward arrow icon).
43016      */
43017     triggerClass : 'x-form-arrow-trigger',
43018     /**
43019      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
43020      */
43021     shadow:'sides',
43022     /**
43023      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
43024      * anchor positions (defaults to 'tl-bl')
43025      */
43026     listAlign: 'tl-bl?',
43027     /**
43028      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43029      */
43030     maxHeight: 300,
43031     /**
43032      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
43033      * query specified by the allQuery config option (defaults to 'query')
43034      */
43035     triggerAction: 'query',
43036     /**
43037      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43038      * (defaults to 4, does not apply if editable = false)
43039      */
43040     minChars : 4,
43041     /**
43042      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43043      * delay (typeAheadDelay) if it matches a known value (defaults to false)
43044      */
43045     typeAhead: false,
43046     /**
43047      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43048      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43049      */
43050     queryDelay: 500,
43051     /**
43052      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43053      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
43054      */
43055     pageSize: 0,
43056     /**
43057      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
43058      * when editable = true (defaults to false)
43059      */
43060     selectOnFocus:false,
43061     /**
43062      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43063      */
43064     queryParam: 'query',
43065     /**
43066      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
43067      * when mode = 'remote' (defaults to 'Loading...')
43068      */
43069     loadingText: 'Loading...',
43070     /**
43071      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43072      */
43073     resizable: false,
43074     /**
43075      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43076      */
43077     handleHeight : 8,
43078     /**
43079      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43080      * traditional select (defaults to true)
43081      */
43082     editable: true,
43083     /**
43084      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43085      */
43086     allQuery: '',
43087     /**
43088      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43089      */
43090     mode: 'remote',
43091     /**
43092      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43093      * listWidth has a higher value)
43094      */
43095     minListWidth : 70,
43096     /**
43097      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43098      * allow the user to set arbitrary text into the field (defaults to false)
43099      */
43100     forceSelection:false,
43101     /**
43102      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43103      * if typeAhead = true (defaults to 250)
43104      */
43105     typeAheadDelay : 250,
43106     /**
43107      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43108      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43109      */
43110     valueNotFoundText : undefined,
43111     /**
43112      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43113      */
43114     blockFocus : false,
43115     
43116     /**
43117      * @cfg {Boolean} disableClear Disable showing of clear button.
43118      */
43119     disableClear : false,
43120     /**
43121      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
43122      */
43123     alwaysQuery : false,
43124     
43125     //private
43126     addicon : false,
43127     editicon: false,
43128     
43129     // element that contains real text value.. (when hidden is used..)
43130      
43131     // private
43132     onRender : function(ct, position)
43133     {
43134         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43135         
43136         if(this.hiddenName){
43137             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43138                     'before', true);
43139             this.hiddenField.value =
43140                 this.hiddenValue !== undefined ? this.hiddenValue :
43141                 this.value !== undefined ? this.value : '';
43142
43143             // prevent input submission
43144             this.el.dom.removeAttribute('name');
43145              
43146              
43147         }
43148         
43149         if(Roo.isGecko){
43150             this.el.dom.setAttribute('autocomplete', 'off');
43151         }
43152
43153         var cls = 'x-combo-list';
43154
43155         this.list = new Roo.Layer({
43156             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43157         });
43158
43159         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43160         this.list.setWidth(lw);
43161         this.list.swallowEvent('mousewheel');
43162         this.assetHeight = 0;
43163
43164         if(this.title){
43165             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43166             this.assetHeight += this.header.getHeight();
43167         }
43168
43169         this.innerList = this.list.createChild({cls:cls+'-inner'});
43170         this.innerList.on('mouseover', this.onViewOver, this);
43171         this.innerList.on('mousemove', this.onViewMove, this);
43172         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43173         
43174         if(this.allowBlank && !this.pageSize && !this.disableClear){
43175             this.footer = this.list.createChild({cls:cls+'-ft'});
43176             this.pageTb = new Roo.Toolbar(this.footer);
43177            
43178         }
43179         if(this.pageSize){
43180             this.footer = this.list.createChild({cls:cls+'-ft'});
43181             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43182                     {pageSize: this.pageSize});
43183             
43184         }
43185         
43186         if (this.pageTb && this.allowBlank && !this.disableClear) {
43187             var _this = this;
43188             this.pageTb.add(new Roo.Toolbar.Fill(), {
43189                 cls: 'x-btn-icon x-btn-clear',
43190                 text: '&#160;',
43191                 handler: function()
43192                 {
43193                     _this.collapse();
43194                     _this.clearValue();
43195                     _this.onSelect(false, -1);
43196                 }
43197             });
43198         }
43199         if (this.footer) {
43200             this.assetHeight += this.footer.getHeight();
43201         }
43202         
43203
43204         if(!this.tpl){
43205             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43206         }
43207
43208         this.view = new Roo.View(this.innerList, this.tpl, {
43209             singleSelect:true,
43210             store: this.store,
43211             selectedClass: this.selectedClass
43212         });
43213
43214         this.view.on('click', this.onViewClick, this);
43215
43216         this.store.on('beforeload', this.onBeforeLoad, this);
43217         this.store.on('load', this.onLoad, this);
43218         this.store.on('loadexception', this.onLoadException, this);
43219
43220         if(this.resizable){
43221             this.resizer = new Roo.Resizable(this.list,  {
43222                pinned:true, handles:'se'
43223             });
43224             this.resizer.on('resize', function(r, w, h){
43225                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43226                 this.listWidth = w;
43227                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43228                 this.restrictHeight();
43229             }, this);
43230             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43231         }
43232         if(!this.editable){
43233             this.editable = true;
43234             this.setEditable(false);
43235         }  
43236         
43237         
43238         if (typeof(this.events.add.listeners) != 'undefined') {
43239             
43240             this.addicon = this.wrap.createChild(
43241                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43242        
43243             this.addicon.on('click', function(e) {
43244                 this.fireEvent('add', this);
43245             }, this);
43246         }
43247         if (typeof(this.events.edit.listeners) != 'undefined') {
43248             
43249             this.editicon = this.wrap.createChild(
43250                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43251             if (this.addicon) {
43252                 this.editicon.setStyle('margin-left', '40px');
43253             }
43254             this.editicon.on('click', function(e) {
43255                 
43256                 // we fire even  if inothing is selected..
43257                 this.fireEvent('edit', this, this.lastData );
43258                 
43259             }, this);
43260         }
43261         
43262         
43263         
43264     },
43265
43266     // private
43267     initEvents : function(){
43268         Roo.form.ComboBox.superclass.initEvents.call(this);
43269
43270         this.keyNav = new Roo.KeyNav(this.el, {
43271             "up" : function(e){
43272                 this.inKeyMode = true;
43273                 this.selectPrev();
43274             },
43275
43276             "down" : function(e){
43277                 if(!this.isExpanded()){
43278                     this.onTriggerClick();
43279                 }else{
43280                     this.inKeyMode = true;
43281                     this.selectNext();
43282                 }
43283             },
43284
43285             "enter" : function(e){
43286                 this.onViewClick();
43287                 //return true;
43288             },
43289
43290             "esc" : function(e){
43291                 this.collapse();
43292             },
43293
43294             "tab" : function(e){
43295                 this.onViewClick(false);
43296                 this.fireEvent("specialkey", this, e);
43297                 return true;
43298             },
43299
43300             scope : this,
43301
43302             doRelay : function(foo, bar, hname){
43303                 if(hname == 'down' || this.scope.isExpanded()){
43304                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43305                 }
43306                 return true;
43307             },
43308
43309             forceKeyDown: true
43310         });
43311         this.queryDelay = Math.max(this.queryDelay || 10,
43312                 this.mode == 'local' ? 10 : 250);
43313         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43314         if(this.typeAhead){
43315             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43316         }
43317         if(this.editable !== false){
43318             this.el.on("keyup", this.onKeyUp, this);
43319         }
43320         if(this.forceSelection){
43321             this.on('blur', this.doForce, this);
43322         }
43323     },
43324
43325     onDestroy : function(){
43326         if(this.view){
43327             this.view.setStore(null);
43328             this.view.el.removeAllListeners();
43329             this.view.el.remove();
43330             this.view.purgeListeners();
43331         }
43332         if(this.list){
43333             this.list.destroy();
43334         }
43335         if(this.store){
43336             this.store.un('beforeload', this.onBeforeLoad, this);
43337             this.store.un('load', this.onLoad, this);
43338             this.store.un('loadexception', this.onLoadException, this);
43339         }
43340         Roo.form.ComboBox.superclass.onDestroy.call(this);
43341     },
43342
43343     // private
43344     fireKey : function(e){
43345         if(e.isNavKeyPress() && !this.list.isVisible()){
43346             this.fireEvent("specialkey", this, e);
43347         }
43348     },
43349
43350     // private
43351     onResize: function(w, h){
43352         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43353         
43354         if(typeof w != 'number'){
43355             // we do not handle it!?!?
43356             return;
43357         }
43358         var tw = this.trigger.getWidth();
43359         tw += this.addicon ? this.addicon.getWidth() : 0;
43360         tw += this.editicon ? this.editicon.getWidth() : 0;
43361         var x = w - tw;
43362         this.el.setWidth( this.adjustWidth('input', x));
43363             
43364         this.trigger.setStyle('left', x+'px');
43365         
43366         if(this.list && this.listWidth === undefined){
43367             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43368             this.list.setWidth(lw);
43369             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43370         }
43371         
43372     
43373         
43374     },
43375
43376     /**
43377      * Allow or prevent the user from directly editing the field text.  If false is passed,
43378      * the user will only be able to select from the items defined in the dropdown list.  This method
43379      * is the runtime equivalent of setting the 'editable' config option at config time.
43380      * @param {Boolean} value True to allow the user to directly edit the field text
43381      */
43382     setEditable : function(value){
43383         if(value == this.editable){
43384             return;
43385         }
43386         this.editable = value;
43387         if(!value){
43388             this.el.dom.setAttribute('readOnly', true);
43389             this.el.on('mousedown', this.onTriggerClick,  this);
43390             this.el.addClass('x-combo-noedit');
43391         }else{
43392             this.el.dom.setAttribute('readOnly', false);
43393             this.el.un('mousedown', this.onTriggerClick,  this);
43394             this.el.removeClass('x-combo-noedit');
43395         }
43396     },
43397
43398     // private
43399     onBeforeLoad : function(){
43400         if(!this.hasFocus){
43401             return;
43402         }
43403         this.innerList.update(this.loadingText ?
43404                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43405         this.restrictHeight();
43406         this.selectedIndex = -1;
43407     },
43408
43409     // private
43410     onLoad : function(){
43411         if(!this.hasFocus){
43412             return;
43413         }
43414         if(this.store.getCount() > 0){
43415             this.expand();
43416             this.restrictHeight();
43417             if(this.lastQuery == this.allQuery){
43418                 if(this.editable){
43419                     this.el.dom.select();
43420                 }
43421                 if(!this.selectByValue(this.value, true)){
43422                     this.select(0, true);
43423                 }
43424             }else{
43425                 this.selectNext();
43426                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43427                     this.taTask.delay(this.typeAheadDelay);
43428                 }
43429             }
43430         }else{
43431             this.onEmptyResults();
43432         }
43433         //this.el.focus();
43434     },
43435     // private
43436     onLoadException : function()
43437     {
43438         this.collapse();
43439         Roo.log(this.store.reader.jsonData);
43440         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43441             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43442         }
43443         
43444         
43445     },
43446     // private
43447     onTypeAhead : function(){
43448         if(this.store.getCount() > 0){
43449             var r = this.store.getAt(0);
43450             var newValue = r.data[this.displayField];
43451             var len = newValue.length;
43452             var selStart = this.getRawValue().length;
43453             if(selStart != len){
43454                 this.setRawValue(newValue);
43455                 this.selectText(selStart, newValue.length);
43456             }
43457         }
43458     },
43459
43460     // private
43461     onSelect : function(record, index){
43462         if(this.fireEvent('beforeselect', this, record, index) !== false){
43463             this.setFromData(index > -1 ? record.data : false);
43464             this.collapse();
43465             this.fireEvent('select', this, record, index);
43466         }
43467     },
43468
43469     /**
43470      * Returns the currently selected field value or empty string if no value is set.
43471      * @return {String} value The selected value
43472      */
43473     getValue : function(){
43474         if(this.valueField){
43475             return typeof this.value != 'undefined' ? this.value : '';
43476         }
43477         return Roo.form.ComboBox.superclass.getValue.call(this);
43478     },
43479
43480     /**
43481      * Clears any text/value currently set in the field
43482      */
43483     clearValue : function(){
43484         if(this.hiddenField){
43485             this.hiddenField.value = '';
43486         }
43487         this.value = '';
43488         this.setRawValue('');
43489         this.lastSelectionText = '';
43490         
43491     },
43492
43493     /**
43494      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43495      * will be displayed in the field.  If the value does not match the data value of an existing item,
43496      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43497      * Otherwise the field will be blank (although the value will still be set).
43498      * @param {String} value The value to match
43499      */
43500     setValue : function(v){
43501         var text = v;
43502         if(this.valueField){
43503             var r = this.findRecord(this.valueField, v);
43504             if(r){
43505                 text = r.data[this.displayField];
43506             }else if(this.valueNotFoundText !== undefined){
43507                 text = this.valueNotFoundText;
43508             }
43509         }
43510         this.lastSelectionText = text;
43511         if(this.hiddenField){
43512             this.hiddenField.value = v;
43513         }
43514         Roo.form.ComboBox.superclass.setValue.call(this, text);
43515         this.value = v;
43516     },
43517     /**
43518      * @property {Object} the last set data for the element
43519      */
43520     
43521     lastData : false,
43522     /**
43523      * Sets the value of the field based on a object which is related to the record format for the store.
43524      * @param {Object} value the value to set as. or false on reset?
43525      */
43526     setFromData : function(o){
43527         var dv = ''; // display value
43528         var vv = ''; // value value..
43529         this.lastData = o;
43530         if (this.displayField) {
43531             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43532         } else {
43533             // this is an error condition!!!
43534             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43535         }
43536         
43537         if(this.valueField){
43538             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43539         }
43540         if(this.hiddenField){
43541             this.hiddenField.value = vv;
43542             
43543             this.lastSelectionText = dv;
43544             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43545             this.value = vv;
43546             return;
43547         }
43548         // no hidden field.. - we store the value in 'value', but still display
43549         // display field!!!!
43550         this.lastSelectionText = dv;
43551         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43552         this.value = vv;
43553         
43554         
43555     },
43556     // private
43557     reset : function(){
43558         // overridden so that last data is reset..
43559         this.setValue(this.resetValue);
43560         this.originalValue = this.getValue();
43561         this.clearInvalid();
43562         this.lastData = false;
43563         if (this.view) {
43564             this.view.clearSelections();
43565         }
43566     },
43567     // private
43568     findRecord : function(prop, value){
43569         var record;
43570         if(this.store.getCount() > 0){
43571             this.store.each(function(r){
43572                 if(r.data[prop] == value){
43573                     record = r;
43574                     return false;
43575                 }
43576                 return true;
43577             });
43578         }
43579         return record;
43580     },
43581     
43582     getName: function()
43583     {
43584         // returns hidden if it's set..
43585         if (!this.rendered) {return ''};
43586         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43587         
43588     },
43589     // private
43590     onViewMove : function(e, t){
43591         this.inKeyMode = false;
43592     },
43593
43594     // private
43595     onViewOver : function(e, t){
43596         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43597             return;
43598         }
43599         var item = this.view.findItemFromChild(t);
43600         if(item){
43601             var index = this.view.indexOf(item);
43602             this.select(index, false);
43603         }
43604     },
43605
43606     // private
43607     onViewClick : function(doFocus)
43608     {
43609         var index = this.view.getSelectedIndexes()[0];
43610         var r = this.store.getAt(index);
43611         if(r){
43612             this.onSelect(r, index);
43613         }
43614         if(doFocus !== false && !this.blockFocus){
43615             this.el.focus();
43616         }
43617     },
43618
43619     // private
43620     restrictHeight : function(){
43621         this.innerList.dom.style.height = '';
43622         var inner = this.innerList.dom;
43623         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43624         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43625         this.list.beginUpdate();
43626         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43627         this.list.alignTo(this.el, this.listAlign);
43628         this.list.endUpdate();
43629     },
43630
43631     // private
43632     onEmptyResults : function(){
43633         this.collapse();
43634     },
43635
43636     /**
43637      * Returns true if the dropdown list is expanded, else false.
43638      */
43639     isExpanded : function(){
43640         return this.list.isVisible();
43641     },
43642
43643     /**
43644      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43645      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43646      * @param {String} value The data value of the item to select
43647      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43648      * selected item if it is not currently in view (defaults to true)
43649      * @return {Boolean} True if the value matched an item in the list, else false
43650      */
43651     selectByValue : function(v, scrollIntoView){
43652         if(v !== undefined && v !== null){
43653             var r = this.findRecord(this.valueField || this.displayField, v);
43654             if(r){
43655                 this.select(this.store.indexOf(r), scrollIntoView);
43656                 return true;
43657             }
43658         }
43659         return false;
43660     },
43661
43662     /**
43663      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43664      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43665      * @param {Number} index The zero-based index of the list item to select
43666      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43667      * selected item if it is not currently in view (defaults to true)
43668      */
43669     select : function(index, scrollIntoView){
43670         this.selectedIndex = index;
43671         this.view.select(index);
43672         if(scrollIntoView !== false){
43673             var el = this.view.getNode(index);
43674             if(el){
43675                 this.innerList.scrollChildIntoView(el, false);
43676             }
43677         }
43678     },
43679
43680     // private
43681     selectNext : function(){
43682         var ct = this.store.getCount();
43683         if(ct > 0){
43684             if(this.selectedIndex == -1){
43685                 this.select(0);
43686             }else if(this.selectedIndex < ct-1){
43687                 this.select(this.selectedIndex+1);
43688             }
43689         }
43690     },
43691
43692     // private
43693     selectPrev : function(){
43694         var ct = this.store.getCount();
43695         if(ct > 0){
43696             if(this.selectedIndex == -1){
43697                 this.select(0);
43698             }else if(this.selectedIndex != 0){
43699                 this.select(this.selectedIndex-1);
43700             }
43701         }
43702     },
43703
43704     // private
43705     onKeyUp : function(e){
43706         if(this.editable !== false && !e.isSpecialKey()){
43707             this.lastKey = e.getKey();
43708             this.dqTask.delay(this.queryDelay);
43709         }
43710     },
43711
43712     // private
43713     validateBlur : function(){
43714         return !this.list || !this.list.isVisible();   
43715     },
43716
43717     // private
43718     initQuery : function(){
43719         this.doQuery(this.getRawValue());
43720     },
43721
43722     // private
43723     doForce : function(){
43724         if(this.el.dom.value.length > 0){
43725             this.el.dom.value =
43726                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43727              
43728         }
43729     },
43730
43731     /**
43732      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43733      * query allowing the query action to be canceled if needed.
43734      * @param {String} query The SQL query to execute
43735      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43736      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43737      * saved in the current store (defaults to false)
43738      */
43739     doQuery : function(q, forceAll){
43740         if(q === undefined || q === null){
43741             q = '';
43742         }
43743         var qe = {
43744             query: q,
43745             forceAll: forceAll,
43746             combo: this,
43747             cancel:false
43748         };
43749         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43750             return false;
43751         }
43752         q = qe.query;
43753         forceAll = qe.forceAll;
43754         if(forceAll === true || (q.length >= this.minChars)){
43755             if(this.lastQuery != q || this.alwaysQuery){
43756                 this.lastQuery = q;
43757                 if(this.mode == 'local'){
43758                     this.selectedIndex = -1;
43759                     if(forceAll){
43760                         this.store.clearFilter();
43761                     }else{
43762                         this.store.filter(this.displayField, q);
43763                     }
43764                     this.onLoad();
43765                 }else{
43766                     this.store.baseParams[this.queryParam] = q;
43767                     this.store.load({
43768                         params: this.getParams(q)
43769                     });
43770                     this.expand();
43771                 }
43772             }else{
43773                 this.selectedIndex = -1;
43774                 this.onLoad();   
43775             }
43776         }
43777     },
43778
43779     // private
43780     getParams : function(q){
43781         var p = {};
43782         //p[this.queryParam] = q;
43783         if(this.pageSize){
43784             p.start = 0;
43785             p.limit = this.pageSize;
43786         }
43787         return p;
43788     },
43789
43790     /**
43791      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43792      */
43793     collapse : function(){
43794         if(!this.isExpanded()){
43795             return;
43796         }
43797         this.list.hide();
43798         Roo.get(document).un('mousedown', this.collapseIf, this);
43799         Roo.get(document).un('mousewheel', this.collapseIf, this);
43800         if (!this.editable) {
43801             Roo.get(document).un('keydown', this.listKeyPress, this);
43802         }
43803         this.fireEvent('collapse', this);
43804     },
43805
43806     // private
43807     collapseIf : function(e){
43808         if(!e.within(this.wrap) && !e.within(this.list)){
43809             this.collapse();
43810         }
43811     },
43812
43813     /**
43814      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43815      */
43816     expand : function(){
43817         if(this.isExpanded() || !this.hasFocus){
43818             return;
43819         }
43820         this.list.alignTo(this.el, this.listAlign);
43821         this.list.show();
43822         Roo.get(document).on('mousedown', this.collapseIf, this);
43823         Roo.get(document).on('mousewheel', this.collapseIf, this);
43824         if (!this.editable) {
43825             Roo.get(document).on('keydown', this.listKeyPress, this);
43826         }
43827         
43828         this.fireEvent('expand', this);
43829     },
43830
43831     // private
43832     // Implements the default empty TriggerField.onTriggerClick function
43833     onTriggerClick : function(){
43834         if(this.disabled){
43835             return;
43836         }
43837         if(this.isExpanded()){
43838             this.collapse();
43839             if (!this.blockFocus) {
43840                 this.el.focus();
43841             }
43842             
43843         }else {
43844             this.hasFocus = true;
43845             if(this.triggerAction == 'all') {
43846                 this.doQuery(this.allQuery, true);
43847             } else {
43848                 this.doQuery(this.getRawValue());
43849             }
43850             if (!this.blockFocus) {
43851                 this.el.focus();
43852             }
43853         }
43854     },
43855     listKeyPress : function(e)
43856     {
43857         //Roo.log('listkeypress');
43858         // scroll to first matching element based on key pres..
43859         if (e.isSpecialKey()) {
43860             return false;
43861         }
43862         var k = String.fromCharCode(e.getKey()).toUpperCase();
43863         //Roo.log(k);
43864         var match  = false;
43865         var csel = this.view.getSelectedNodes();
43866         var cselitem = false;
43867         if (csel.length) {
43868             var ix = this.view.indexOf(csel[0]);
43869             cselitem  = this.store.getAt(ix);
43870             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43871                 cselitem = false;
43872             }
43873             
43874         }
43875         
43876         this.store.each(function(v) { 
43877             if (cselitem) {
43878                 // start at existing selection.
43879                 if (cselitem.id == v.id) {
43880                     cselitem = false;
43881                 }
43882                 return;
43883             }
43884                 
43885             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43886                 match = this.store.indexOf(v);
43887                 return false;
43888             }
43889         }, this);
43890         
43891         if (match === false) {
43892             return true; // no more action?
43893         }
43894         // scroll to?
43895         this.view.select(match);
43896         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43897         sn.scrollIntoView(sn.dom.parentNode, false);
43898     } 
43899
43900     /** 
43901     * @cfg {Boolean} grow 
43902     * @hide 
43903     */
43904     /** 
43905     * @cfg {Number} growMin 
43906     * @hide 
43907     */
43908     /** 
43909     * @cfg {Number} growMax 
43910     * @hide 
43911     */
43912     /**
43913      * @hide
43914      * @method autoSize
43915      */
43916 });/*
43917  * Copyright(c) 2010-2012, Roo J Solutions Limited
43918  *
43919  * Licence LGPL
43920  *
43921  */
43922
43923 /**
43924  * @class Roo.form.ComboBoxArray
43925  * @extends Roo.form.TextField
43926  * A facebook style adder... for lists of email / people / countries  etc...
43927  * pick multiple items from a combo box, and shows each one.
43928  *
43929  *  Fred [x]  Brian [x]  [Pick another |v]
43930  *
43931  *
43932  *  For this to work: it needs various extra information
43933  *    - normal combo problay has
43934  *      name, hiddenName
43935  *    + displayField, valueField
43936  *
43937  *    For our purpose...
43938  *
43939  *
43940  *   If we change from 'extends' to wrapping...
43941  *   
43942  *  
43943  *
43944  
43945  
43946  * @constructor
43947  * Create a new ComboBoxArray.
43948  * @param {Object} config Configuration options
43949  */
43950  
43951
43952 Roo.form.ComboBoxArray = function(config)
43953 {
43954     this.addEvents({
43955         /**
43956          * @event beforeremove
43957          * Fires before remove the value from the list
43958              * @param {Roo.form.ComboBoxArray} _self This combo box array
43959              * @param {Roo.form.ComboBoxArray.Item} item removed item
43960              */
43961         'beforeremove' : true,
43962         /**
43963          * @event remove
43964          * Fires when remove the value from the list
43965              * @param {Roo.form.ComboBoxArray} _self This combo box array
43966              * @param {Roo.form.ComboBoxArray.Item} item removed item
43967              */
43968         'remove' : true
43969         
43970         
43971     });
43972     
43973     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43974     
43975     this.items = new Roo.util.MixedCollection(false);
43976     
43977     // construct the child combo...
43978     
43979     
43980     
43981     
43982    
43983     
43984 }
43985
43986  
43987 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43988
43989     /**
43990      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43991      */
43992     
43993     lastData : false,
43994     
43995     // behavies liek a hiddne field
43996     inputType:      'hidden',
43997     /**
43998      * @cfg {Number} width The width of the box that displays the selected element
43999      */ 
44000     width:          300,
44001
44002     
44003     
44004     /**
44005      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
44006      */
44007     name : false,
44008     /**
44009      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
44010      */
44011     hiddenName : false,
44012       /**
44013      * @cfg {String} seperator    The value seperator normally ',' 
44014      */
44015     seperator : ',',
44016     
44017     // private the array of items that are displayed..
44018     items  : false,
44019     // private - the hidden field el.
44020     hiddenEl : false,
44021     // private - the filed el..
44022     el : false,
44023     
44024     //validateValue : function() { return true; }, // all values are ok!
44025     //onAddClick: function() { },
44026     
44027     onRender : function(ct, position) 
44028     {
44029         
44030         // create the standard hidden element
44031         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44032         
44033         
44034         // give fake names to child combo;
44035         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44036         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44037         
44038         this.combo = Roo.factory(this.combo, Roo.form);
44039         this.combo.onRender(ct, position);
44040         if (typeof(this.combo.width) != 'undefined') {
44041             this.combo.onResize(this.combo.width,0);
44042         }
44043         
44044         this.combo.initEvents();
44045         
44046         // assigned so form know we need to do this..
44047         this.store          = this.combo.store;
44048         this.valueField     = this.combo.valueField;
44049         this.displayField   = this.combo.displayField ;
44050         
44051         
44052         this.combo.wrap.addClass('x-cbarray-grp');
44053         
44054         var cbwrap = this.combo.wrap.createChild(
44055             {tag: 'div', cls: 'x-cbarray-cb'},
44056             this.combo.el.dom
44057         );
44058         
44059              
44060         this.hiddenEl = this.combo.wrap.createChild({
44061             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
44062         });
44063         this.el = this.combo.wrap.createChild({
44064             tag: 'input',  type:'hidden' , name: this.name, value : ''
44065         });
44066          //   this.el.dom.removeAttribute("name");
44067         
44068         
44069         this.outerWrap = this.combo.wrap;
44070         this.wrap = cbwrap;
44071         
44072         this.outerWrap.setWidth(this.width);
44073         this.outerWrap.dom.removeChild(this.el.dom);
44074         
44075         this.wrap.dom.appendChild(this.el.dom);
44076         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44077         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44078         
44079         this.combo.trigger.setStyle('position','relative');
44080         this.combo.trigger.setStyle('left', '0px');
44081         this.combo.trigger.setStyle('top', '2px');
44082         
44083         this.combo.el.setStyle('vertical-align', 'text-bottom');
44084         
44085         //this.trigger.setStyle('vertical-align', 'top');
44086         
44087         // this should use the code from combo really... on('add' ....)
44088         if (this.adder) {
44089             
44090         
44091             this.adder = this.outerWrap.createChild(
44092                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
44093             var _t = this;
44094             this.adder.on('click', function(e) {
44095                 _t.fireEvent('adderclick', this, e);
44096             }, _t);
44097         }
44098         //var _t = this;
44099         //this.adder.on('click', this.onAddClick, _t);
44100         
44101         
44102         this.combo.on('select', function(cb, rec, ix) {
44103             this.addItem(rec.data);
44104             
44105             cb.setValue('');
44106             cb.el.dom.value = '';
44107             //cb.lastData = rec.data;
44108             // add to list
44109             
44110         }, this);
44111         
44112         
44113     },
44114     
44115     
44116     getName: function()
44117     {
44118         // returns hidden if it's set..
44119         if (!this.rendered) {return ''};
44120         return  this.hiddenName ? this.hiddenName : this.name;
44121         
44122     },
44123     
44124     
44125     onResize: function(w, h){
44126         
44127         return;
44128         // not sure if this is needed..
44129         //this.combo.onResize(w,h);
44130         
44131         if(typeof w != 'number'){
44132             // we do not handle it!?!?
44133             return;
44134         }
44135         var tw = this.combo.trigger.getWidth();
44136         tw += this.addicon ? this.addicon.getWidth() : 0;
44137         tw += this.editicon ? this.editicon.getWidth() : 0;
44138         var x = w - tw;
44139         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44140             
44141         this.combo.trigger.setStyle('left', '0px');
44142         
44143         if(this.list && this.listWidth === undefined){
44144             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44145             this.list.setWidth(lw);
44146             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44147         }
44148         
44149     
44150         
44151     },
44152     
44153     addItem: function(rec)
44154     {
44155         var valueField = this.combo.valueField;
44156         var displayField = this.combo.displayField;
44157         
44158         if (this.items.indexOfKey(rec[valueField]) > -1) {
44159             //console.log("GOT " + rec.data.id);
44160             return;
44161         }
44162         
44163         var x = new Roo.form.ComboBoxArray.Item({
44164             //id : rec[this.idField],
44165             data : rec,
44166             displayField : displayField ,
44167             tipField : displayField ,
44168             cb : this
44169         });
44170         // use the 
44171         this.items.add(rec[valueField],x);
44172         // add it before the element..
44173         this.updateHiddenEl();
44174         x.render(this.outerWrap, this.wrap.dom);
44175         // add the image handler..
44176     },
44177     
44178     updateHiddenEl : function()
44179     {
44180         this.validate();
44181         if (!this.hiddenEl) {
44182             return;
44183         }
44184         var ar = [];
44185         var idField = this.combo.valueField;
44186         
44187         this.items.each(function(f) {
44188             ar.push(f.data[idField]);
44189         });
44190         this.hiddenEl.dom.value = ar.join(this.seperator);
44191         this.validate();
44192     },
44193     
44194     reset : function()
44195     {
44196         this.items.clear();
44197         
44198         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44199            el.remove();
44200         });
44201         
44202         this.el.dom.value = '';
44203         if (this.hiddenEl) {
44204             this.hiddenEl.dom.value = '';
44205         }
44206         
44207     },
44208     getValue: function()
44209     {
44210         return this.hiddenEl ? this.hiddenEl.dom.value : '';
44211     },
44212     setValue: function(v) // not a valid action - must use addItems..
44213     {
44214         
44215         this.reset();
44216          
44217         if (this.store.isLocal && (typeof(v) == 'string')) {
44218             // then we can use the store to find the values..
44219             // comma seperated at present.. this needs to allow JSON based encoding..
44220             this.hiddenEl.value  = v;
44221             var v_ar = [];
44222             Roo.each(v.split(this.seperator), function(k) {
44223                 Roo.log("CHECK " + this.valueField + ',' + k);
44224                 var li = this.store.query(this.valueField, k);
44225                 if (!li.length) {
44226                     return;
44227                 }
44228                 var add = {};
44229                 add[this.valueField] = k;
44230                 add[this.displayField] = li.item(0).data[this.displayField];
44231                 
44232                 this.addItem(add);
44233             }, this) 
44234              
44235         }
44236         if (typeof(v) == 'object' ) {
44237             // then let's assume it's an array of objects..
44238             Roo.each(v, function(l) {
44239                 var add = l;
44240                 if (typeof(l) == 'string') {
44241                     add = {};
44242                     add[this.valueField] = l;
44243                     add[this.displayField] = l
44244                 }
44245                 this.addItem(add);
44246             }, this);
44247              
44248         }
44249         
44250         
44251     },
44252     setFromData: function(v)
44253     {
44254         // this recieves an object, if setValues is called.
44255         this.reset();
44256         this.el.dom.value = v[this.displayField];
44257         this.hiddenEl.dom.value = v[this.valueField];
44258         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44259             return;
44260         }
44261         var kv = v[this.valueField];
44262         var dv = v[this.displayField];
44263         kv = typeof(kv) != 'string' ? '' : kv;
44264         dv = typeof(dv) != 'string' ? '' : dv;
44265         
44266         
44267         var keys = kv.split(this.seperator);
44268         var display = dv.split(this.seperator);
44269         for (var i = 0 ; i < keys.length; i++) {
44270             add = {};
44271             add[this.valueField] = keys[i];
44272             add[this.displayField] = display[i];
44273             this.addItem(add);
44274         }
44275       
44276         
44277     },
44278     
44279     /**
44280      * Validates the combox array value
44281      * @return {Boolean} True if the value is valid, else false
44282      */
44283     validate : function(){
44284         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44285             this.clearInvalid();
44286             return true;
44287         }
44288         return false;
44289     },
44290     
44291     validateValue : function(value){
44292         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44293         
44294     },
44295     
44296     /*@
44297      * overide
44298      * 
44299      */
44300     isDirty : function() {
44301         if(this.disabled) {
44302             return false;
44303         }
44304         
44305         try {
44306             var d = Roo.decode(String(this.originalValue));
44307         } catch (e) {
44308             return String(this.getValue()) !== String(this.originalValue);
44309         }
44310         
44311         var originalValue = [];
44312         
44313         for (var i = 0; i < d.length; i++){
44314             originalValue.push(d[i][this.valueField]);
44315         }
44316         
44317         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44318         
44319     }
44320     
44321 });
44322
44323
44324
44325 /**
44326  * @class Roo.form.ComboBoxArray.Item
44327  * @extends Roo.BoxComponent
44328  * A selected item in the list
44329  *  Fred [x]  Brian [x]  [Pick another |v]
44330  * 
44331  * @constructor
44332  * Create a new item.
44333  * @param {Object} config Configuration options
44334  */
44335  
44336 Roo.form.ComboBoxArray.Item = function(config) {
44337     config.id = Roo.id();
44338     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44339 }
44340
44341 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44342     data : {},
44343     cb: false,
44344     displayField : false,
44345     tipField : false,
44346     
44347     
44348     defaultAutoCreate : {
44349         tag: 'div',
44350         cls: 'x-cbarray-item',
44351         cn : [ 
44352             { tag: 'div' },
44353             {
44354                 tag: 'img',
44355                 width:16,
44356                 height : 16,
44357                 src : Roo.BLANK_IMAGE_URL ,
44358                 align: 'center'
44359             }
44360         ]
44361         
44362     },
44363     
44364  
44365     onRender : function(ct, position)
44366     {
44367         Roo.form.Field.superclass.onRender.call(this, ct, position);
44368         
44369         if(!this.el){
44370             var cfg = this.getAutoCreate();
44371             this.el = ct.createChild(cfg, position);
44372         }
44373         
44374         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44375         
44376         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44377             this.cb.renderer(this.data) :
44378             String.format('{0}',this.data[this.displayField]);
44379         
44380             
44381         this.el.child('div').dom.setAttribute('qtip',
44382                         String.format('{0}',this.data[this.tipField])
44383         );
44384         
44385         this.el.child('img').on('click', this.remove, this);
44386         
44387     },
44388    
44389     remove : function()
44390     {
44391         if(this.cb.disabled){
44392             return;
44393         }
44394         
44395         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44396             this.cb.items.remove(this);
44397             this.el.child('img').un('click', this.remove, this);
44398             this.el.remove();
44399             this.cb.updateHiddenEl();
44400
44401             this.cb.fireEvent('remove', this.cb, this);
44402         }
44403         
44404     }
44405 });/*
44406  * RooJS Library 1.1.1
44407  * Copyright(c) 2008-2011  Alan Knowles
44408  *
44409  * License - LGPL
44410  */
44411  
44412
44413 /**
44414  * @class Roo.form.ComboNested
44415  * @extends Roo.form.ComboBox
44416  * A combobox for that allows selection of nested items in a list,
44417  * eg.
44418  *
44419  *  Book
44420  *    -> red
44421  *    -> green
44422  *  Table
44423  *    -> square
44424  *      ->red
44425  *      ->green
44426  *    -> rectangle
44427  *      ->green
44428  *      
44429  * 
44430  * @constructor
44431  * Create a new ComboNested
44432  * @param {Object} config Configuration options
44433  */
44434 Roo.form.ComboNested = function(config){
44435     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44436     // should verify some data...
44437     // like
44438     // hiddenName = required..
44439     // displayField = required
44440     // valudField == required
44441     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44442     var _t = this;
44443     Roo.each(req, function(e) {
44444         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44445             throw "Roo.form.ComboNested : missing value for: " + e;
44446         }
44447     });
44448      
44449     
44450 };
44451
44452 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44453    
44454     /*
44455      * @config {Number} max Number of columns to show
44456      */
44457     
44458     maxColumns : 3,
44459    
44460     list : null, // the outermost div..
44461     innerLists : null, // the
44462     views : null,
44463     stores : null,
44464     // private
44465     loadingChildren : false,
44466     
44467     onRender : function(ct, position)
44468     {
44469         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44470         
44471         if(this.hiddenName){
44472             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44473                     'before', true);
44474             this.hiddenField.value =
44475                 this.hiddenValue !== undefined ? this.hiddenValue :
44476                 this.value !== undefined ? this.value : '';
44477
44478             // prevent input submission
44479             this.el.dom.removeAttribute('name');
44480              
44481              
44482         }
44483         
44484         if(Roo.isGecko){
44485             this.el.dom.setAttribute('autocomplete', 'off');
44486         }
44487
44488         var cls = 'x-combo-list';
44489
44490         this.list = new Roo.Layer({
44491             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44492         });
44493
44494         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44495         this.list.setWidth(lw);
44496         this.list.swallowEvent('mousewheel');
44497         this.assetHeight = 0;
44498
44499         if(this.title){
44500             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44501             this.assetHeight += this.header.getHeight();
44502         }
44503         this.innerLists = [];
44504         this.views = [];
44505         this.stores = [];
44506         for (var i =0 ; i < this.maxColumns; i++) {
44507             this.onRenderList( cls, i);
44508         }
44509         
44510         // always needs footer, as we are going to have an 'OK' button.
44511         this.footer = this.list.createChild({cls:cls+'-ft'});
44512         this.pageTb = new Roo.Toolbar(this.footer);  
44513         var _this = this;
44514         this.pageTb.add(  {
44515             
44516             text: 'Done',
44517             handler: function()
44518             {
44519                 _this.collapse();
44520             }
44521         });
44522         
44523         if ( this.allowBlank && !this.disableClear) {
44524             
44525             this.pageTb.add(new Roo.Toolbar.Fill(), {
44526                 cls: 'x-btn-icon x-btn-clear',
44527                 text: '&#160;',
44528                 handler: function()
44529                 {
44530                     _this.collapse();
44531                     _this.clearValue();
44532                     _this.onSelect(false, -1);
44533                 }
44534             });
44535         }
44536         if (this.footer) {
44537             this.assetHeight += this.footer.getHeight();
44538         }
44539         
44540     },
44541     onRenderList : function (  cls, i)
44542     {
44543         
44544         var lw = Math.floor(
44545                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44546         );
44547         
44548         this.list.setWidth(lw); // default to '1'
44549
44550         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44551         //il.on('mouseover', this.onViewOver, this, { list:  i });
44552         //il.on('mousemove', this.onViewMove, this, { list:  i });
44553         il.setWidth(lw);
44554         il.setStyle({ 'overflow-x' : 'hidden'});
44555
44556         if(!this.tpl){
44557             this.tpl = new Roo.Template({
44558                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44559                 isEmpty: function (value, allValues) {
44560                     //Roo.log(value);
44561                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44562                     return dl ? 'has-children' : 'no-children'
44563                 }
44564             });
44565         }
44566         
44567         var store  = this.store;
44568         if (i > 0) {
44569             store  = new Roo.data.SimpleStore({
44570                 //fields : this.store.reader.meta.fields,
44571                 reader : this.store.reader,
44572                 data : [ ]
44573             });
44574         }
44575         this.stores[i]  = store;
44576                   
44577         var view = this.views[i] = new Roo.View(
44578             il,
44579             this.tpl,
44580             {
44581                 singleSelect:true,
44582                 store: store,
44583                 selectedClass: this.selectedClass
44584             }
44585         );
44586         view.getEl().setWidth(lw);
44587         view.getEl().setStyle({
44588             position: i < 1 ? 'relative' : 'absolute',
44589             top: 0,
44590             left: (i * lw ) + 'px',
44591             display : i > 0 ? 'none' : 'block'
44592         });
44593         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44594         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44595         //view.on('click', this.onViewClick, this, { list : i });
44596
44597         store.on('beforeload', this.onBeforeLoad, this);
44598         store.on('load',  this.onLoad, this, { list  : i});
44599         store.on('loadexception', this.onLoadException, this);
44600
44601         // hide the other vies..
44602         
44603         
44604         
44605     },
44606       
44607     restrictHeight : function()
44608     {
44609         var mh = 0;
44610         Roo.each(this.innerLists, function(il,i) {
44611             var el = this.views[i].getEl();
44612             el.dom.style.height = '';
44613             var inner = el.dom;
44614             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44615             // only adjust heights on other ones..
44616             mh = Math.max(h, mh);
44617             if (i < 1) {
44618                 
44619                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44620                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44621                
44622             }
44623             
44624             
44625         }, this);
44626         
44627         this.list.beginUpdate();
44628         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44629         this.list.alignTo(this.el, this.listAlign);
44630         this.list.endUpdate();
44631         
44632     },
44633      
44634     
44635     // -- store handlers..
44636     // private
44637     onBeforeLoad : function()
44638     {
44639         if(!this.hasFocus){
44640             return;
44641         }
44642         this.innerLists[0].update(this.loadingText ?
44643                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44644         this.restrictHeight();
44645         this.selectedIndex = -1;
44646     },
44647     // private
44648     onLoad : function(a,b,c,d)
44649     {
44650         if (!this.loadingChildren) {
44651             // then we are loading the top level. - hide the children
44652             for (var i = 1;i < this.views.length; i++) {
44653                 this.views[i].getEl().setStyle({ display : 'none' });
44654             }
44655             var lw = Math.floor(
44656                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44657             );
44658         
44659              this.list.setWidth(lw); // default to '1'
44660
44661             
44662         }
44663         if(!this.hasFocus){
44664             return;
44665         }
44666         
44667         if(this.store.getCount() > 0) {
44668             this.expand();
44669             this.restrictHeight();   
44670         } else {
44671             this.onEmptyResults();
44672         }
44673         
44674         if (!this.loadingChildren) {
44675             this.selectActive();
44676         }
44677         /*
44678         this.stores[1].loadData([]);
44679         this.stores[2].loadData([]);
44680         this.views
44681         */    
44682     
44683         //this.el.focus();
44684     },
44685     
44686     
44687     // private
44688     onLoadException : function()
44689     {
44690         this.collapse();
44691         Roo.log(this.store.reader.jsonData);
44692         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44693             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44694         }
44695         
44696         
44697     },
44698     // no cleaning of leading spaces on blur here.
44699     cleanLeadingSpace : function(e) { },
44700     
44701
44702     onSelectChange : function (view, sels, opts )
44703     {
44704         var ix = view.getSelectedIndexes();
44705          
44706         if (opts.list > this.maxColumns - 2) {
44707             if (view.store.getCount()<  1) {
44708                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44709
44710             } else  {
44711                 if (ix.length) {
44712                     // used to clear ?? but if we are loading unselected 
44713                     this.setFromData(view.store.getAt(ix[0]).data);
44714                 }
44715                 
44716             }
44717             
44718             return;
44719         }
44720         
44721         if (!ix.length) {
44722             // this get's fired when trigger opens..
44723            // this.setFromData({});
44724             var str = this.stores[opts.list+1];
44725             str.data.clear(); // removeall wihtout the fire events..
44726             return;
44727         }
44728         
44729         var rec = view.store.getAt(ix[0]);
44730          
44731         this.setFromData(rec.data);
44732         this.fireEvent('select', this, rec, ix[0]);
44733         
44734         var lw = Math.floor(
44735              (
44736                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44737              ) / this.maxColumns
44738         );
44739         this.loadingChildren = true;
44740         this.stores[opts.list+1].loadDataFromChildren( rec );
44741         this.loadingChildren = false;
44742         var dl = this.stores[opts.list+1]. getTotalCount();
44743         
44744         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44745         
44746         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44747         for (var i = opts.list+2; i < this.views.length;i++) {
44748             this.views[i].getEl().setStyle({ display : 'none' });
44749         }
44750         
44751         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44752         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44753         
44754         if (this.isLoading) {
44755            // this.selectActive(opts.list);
44756         }
44757          
44758     },
44759     
44760     
44761     
44762     
44763     onDoubleClick : function()
44764     {
44765         this.collapse(); //??
44766     },
44767     
44768      
44769     
44770     
44771     
44772     // private
44773     recordToStack : function(store, prop, value, stack)
44774     {
44775         var cstore = new Roo.data.SimpleStore({
44776             //fields : this.store.reader.meta.fields, // we need array reader.. for
44777             reader : this.store.reader,
44778             data : [ ]
44779         });
44780         var _this = this;
44781         var record  = false;
44782         var srec = false;
44783         if(store.getCount() < 1){
44784             return false;
44785         }
44786         store.each(function(r){
44787             if(r.data[prop] == value){
44788                 record = r;
44789             srec = r;
44790                 return false;
44791             }
44792             if (r.data.cn && r.data.cn.length) {
44793                 cstore.loadDataFromChildren( r);
44794                 var cret = _this.recordToStack(cstore, prop, value, stack);
44795                 if (cret !== false) {
44796                     record = cret;
44797                     srec = r;
44798                     return false;
44799                 }
44800             }
44801              
44802             return true;
44803         });
44804         if (record == false) {
44805             return false
44806         }
44807         stack.unshift(srec);
44808         return record;
44809     },
44810     
44811     /*
44812      * find the stack of stores that match our value.
44813      *
44814      * 
44815      */
44816     
44817     selectActive : function ()
44818     {
44819         // if store is not loaded, then we will need to wait for that to happen first.
44820         var stack = [];
44821         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44822         for (var i = 0; i < stack.length; i++ ) {
44823             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44824         }
44825         
44826     }
44827         
44828          
44829     
44830     
44831     
44832     
44833 });/*
44834  * Based on:
44835  * Ext JS Library 1.1.1
44836  * Copyright(c) 2006-2007, Ext JS, LLC.
44837  *
44838  * Originally Released Under LGPL - original licence link has changed is not relivant.
44839  *
44840  * Fork - LGPL
44841  * <script type="text/javascript">
44842  */
44843 /**
44844  * @class Roo.form.Checkbox
44845  * @extends Roo.form.Field
44846  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44847  * @constructor
44848  * Creates a new Checkbox
44849  * @param {Object} config Configuration options
44850  */
44851 Roo.form.Checkbox = function(config){
44852     Roo.form.Checkbox.superclass.constructor.call(this, config);
44853     this.addEvents({
44854         /**
44855          * @event check
44856          * Fires when the checkbox is checked or unchecked.
44857              * @param {Roo.form.Checkbox} this This checkbox
44858              * @param {Boolean} checked The new checked value
44859              */
44860         check : true
44861     });
44862 };
44863
44864 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44865     /**
44866      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44867      */
44868     focusClass : undefined,
44869     /**
44870      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44871      */
44872     fieldClass: "x-form-field",
44873     /**
44874      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44875      */
44876     checked: false,
44877     /**
44878      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44879      * {tag: "input", type: "checkbox", autocomplete: "off"})
44880      */
44881     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44882     /**
44883      * @cfg {String} boxLabel The text that appears beside the checkbox
44884      */
44885     boxLabel : "",
44886     /**
44887      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44888      */  
44889     inputValue : '1',
44890     /**
44891      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44892      */
44893      valueOff: '0', // value when not checked..
44894
44895     actionMode : 'viewEl', 
44896     //
44897     // private
44898     itemCls : 'x-menu-check-item x-form-item',
44899     groupClass : 'x-menu-group-item',
44900     inputType : 'hidden',
44901     
44902     
44903     inSetChecked: false, // check that we are not calling self...
44904     
44905     inputElement: false, // real input element?
44906     basedOn: false, // ????
44907     
44908     isFormField: true, // not sure where this is needed!!!!
44909
44910     onResize : function(){
44911         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44912         if(!this.boxLabel){
44913             this.el.alignTo(this.wrap, 'c-c');
44914         }
44915     },
44916
44917     initEvents : function(){
44918         Roo.form.Checkbox.superclass.initEvents.call(this);
44919         this.el.on("click", this.onClick,  this);
44920         this.el.on("change", this.onClick,  this);
44921     },
44922
44923
44924     getResizeEl : function(){
44925         return this.wrap;
44926     },
44927
44928     getPositionEl : function(){
44929         return this.wrap;
44930     },
44931
44932     // private
44933     onRender : function(ct, position){
44934         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44935         /*
44936         if(this.inputValue !== undefined){
44937             this.el.dom.value = this.inputValue;
44938         }
44939         */
44940         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44941         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44942         var viewEl = this.wrap.createChild({ 
44943             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44944         this.viewEl = viewEl;   
44945         this.wrap.on('click', this.onClick,  this); 
44946         
44947         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44948         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44949         
44950         
44951         
44952         if(this.boxLabel){
44953             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44954         //    viewEl.on('click', this.onClick,  this); 
44955         }
44956         //if(this.checked){
44957             this.setChecked(this.checked);
44958         //}else{
44959             //this.checked = this.el.dom;
44960         //}
44961
44962     },
44963
44964     // private
44965     initValue : Roo.emptyFn,
44966
44967     /**
44968      * Returns the checked state of the checkbox.
44969      * @return {Boolean} True if checked, else false
44970      */
44971     getValue : function(){
44972         if(this.el){
44973             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44974         }
44975         return this.valueOff;
44976         
44977     },
44978
44979         // private
44980     onClick : function(){ 
44981         if (this.disabled) {
44982             return;
44983         }
44984         this.setChecked(!this.checked);
44985
44986         //if(this.el.dom.checked != this.checked){
44987         //    this.setValue(this.el.dom.checked);
44988        // }
44989     },
44990
44991     /**
44992      * Sets the checked state of the checkbox.
44993      * On is always based on a string comparison between inputValue and the param.
44994      * @param {Boolean/String} value - the value to set 
44995      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44996      */
44997     setValue : function(v,suppressEvent){
44998         
44999         
45000         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
45001         //if(this.el && this.el.dom){
45002         //    this.el.dom.checked = this.checked;
45003         //    this.el.dom.defaultChecked = this.checked;
45004         //}
45005         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45006         //this.fireEvent("check", this, this.checked);
45007     },
45008     // private..
45009     setChecked : function(state,suppressEvent)
45010     {
45011         if (this.inSetChecked) {
45012             this.checked = state;
45013             return;
45014         }
45015         
45016     
45017         if(this.wrap){
45018             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45019         }
45020         this.checked = state;
45021         if(suppressEvent !== true){
45022             this.fireEvent('check', this, state);
45023         }
45024         this.inSetChecked = true;
45025         this.el.dom.value = state ? this.inputValue : this.valueOff;
45026         this.inSetChecked = false;
45027         
45028     },
45029     // handle setting of hidden value by some other method!!?!?
45030     setFromHidden: function()
45031     {
45032         if(!this.el){
45033             return;
45034         }
45035         //console.log("SET FROM HIDDEN");
45036         //alert('setFrom hidden');
45037         this.setValue(this.el.dom.value);
45038     },
45039     
45040     onDestroy : function()
45041     {
45042         if(this.viewEl){
45043             Roo.get(this.viewEl).remove();
45044         }
45045          
45046         Roo.form.Checkbox.superclass.onDestroy.call(this);
45047     },
45048     
45049     setBoxLabel : function(str)
45050     {
45051         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45052     }
45053
45054 });/*
45055  * Based on:
45056  * Ext JS Library 1.1.1
45057  * Copyright(c) 2006-2007, Ext JS, LLC.
45058  *
45059  * Originally Released Under LGPL - original licence link has changed is not relivant.
45060  *
45061  * Fork - LGPL
45062  * <script type="text/javascript">
45063  */
45064  
45065 /**
45066  * @class Roo.form.Radio
45067  * @extends Roo.form.Checkbox
45068  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
45069  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45070  * @constructor
45071  * Creates a new Radio
45072  * @param {Object} config Configuration options
45073  */
45074 Roo.form.Radio = function(){
45075     Roo.form.Radio.superclass.constructor.apply(this, arguments);
45076 };
45077 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45078     inputType: 'radio',
45079
45080     /**
45081      * If this radio is part of a group, it will return the selected value
45082      * @return {String}
45083      */
45084     getGroupValue : function(){
45085         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45086     },
45087     
45088     
45089     onRender : function(ct, position){
45090         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45091         
45092         if(this.inputValue !== undefined){
45093             this.el.dom.value = this.inputValue;
45094         }
45095          
45096         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45097         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45098         //var viewEl = this.wrap.createChild({ 
45099         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45100         //this.viewEl = viewEl;   
45101         //this.wrap.on('click', this.onClick,  this); 
45102         
45103         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45104         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
45105         
45106         
45107         
45108         if(this.boxLabel){
45109             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45110         //    viewEl.on('click', this.onClick,  this); 
45111         }
45112          if(this.checked){
45113             this.el.dom.checked =   'checked' ;
45114         }
45115          
45116     } 
45117     
45118     
45119 });Roo.rtf = {}; // namespace
45120 Roo.rtf.Hex = function(hex)
45121 {
45122     this.hexstr = hex;
45123 };
45124 Roo.rtf.Paragraph = function(opts)
45125 {
45126     this.content = []; ///??? is that used?
45127 };Roo.rtf.Span = function(opts)
45128 {
45129     this.value = opts.value;
45130 };
45131
45132 Roo.rtf.Group = function(parent)
45133 {
45134     // we dont want to acutally store parent - it will make debug a nightmare..
45135     this.content = [];
45136     this.cn  = [];
45137      
45138        
45139     
45140 };
45141
45142 Roo.rtf.Group.prototype = {
45143     ignorable : false,
45144     content: false,
45145     cn: false,
45146     addContent : function(node) {
45147         // could set styles...
45148         this.content.push(node);
45149     },
45150     addChild : function(cn)
45151     {
45152         this.cn.push(cn);
45153     },
45154     // only for images really...
45155     toDataURL : function()
45156     {
45157         var mimetype = false;
45158         switch(true) {
45159             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
45160                 mimetype = "image/png";
45161                 break;
45162              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45163                 mimetype = "image/jpeg";
45164                 break;
45165             default :
45166                 return 'about:blank'; // ?? error?
45167         }
45168         
45169         
45170         var hexstring = this.content[this.content.length-1].value;
45171         
45172         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45173             return String.fromCharCode(parseInt(a, 16));
45174         }).join(""));
45175     }
45176     
45177 };
45178 // this looks like it's normally the {rtf{ .... }}
45179 Roo.rtf.Document = function()
45180 {
45181     // we dont want to acutally store parent - it will make debug a nightmare..
45182     this.rtlch  = [];
45183     this.content = [];
45184     this.cn = [];
45185     
45186 };
45187 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
45188     addChild : function(cn)
45189     {
45190         this.cn.push(cn);
45191         switch(cn.type) {
45192             case 'rtlch': // most content seems to be inside this??
45193             case 'listtext':
45194             case 'shpinst':
45195                 this.rtlch.push(cn);
45196                 return;
45197             default:
45198                 this[cn.type] = cn;
45199         }
45200         
45201     },
45202     
45203     getElementsByType : function(type)
45204     {
45205         var ret =  [];
45206         this._getElementsByType(type, ret, this.cn, 'rtf');
45207         return ret;
45208     },
45209     _getElementsByType : function (type, ret, search_array, path)
45210     {
45211         search_array.forEach(function(n,i) {
45212             if (n.type == type) {
45213                 n.path = path + '/' + n.type + ':' + i;
45214                 ret.push(n);
45215             }
45216             if (n.cn.length > 0) {
45217                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45218             }
45219         },this);
45220     }
45221     
45222 });
45223  
45224 Roo.rtf.Ctrl = function(opts)
45225 {
45226     this.value = opts.value;
45227     this.param = opts.param;
45228 };
45229 /**
45230  *
45231  *
45232  * based on this https://github.com/iarna/rtf-parser
45233  * it's really only designed to extract pict from pasted RTF 
45234  *
45235  * usage:
45236  *
45237  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45238  *  
45239  *
45240  */
45241
45242  
45243
45244
45245
45246 Roo.rtf.Parser = function(text) {
45247     //super({objectMode: true})
45248     this.text = '';
45249     this.parserState = this.parseText;
45250     
45251     // these are for interpeter...
45252     this.doc = {};
45253     ///this.parserState = this.parseTop
45254     this.groupStack = [];
45255     this.hexStore = [];
45256     this.doc = false;
45257     
45258     this.groups = []; // where we put the return.
45259     
45260     for (var ii = 0; ii < text.length; ++ii) {
45261         ++this.cpos;
45262         
45263         if (text[ii] === '\n') {
45264             ++this.row;
45265             this.col = 1;
45266         } else {
45267             ++this.col;
45268         }
45269         this.parserState(text[ii]);
45270     }
45271     
45272     
45273     
45274 };
45275 Roo.rtf.Parser.prototype = {
45276     text : '', // string being parsed..
45277     controlWord : '',
45278     controlWordParam :  '',
45279     hexChar : '',
45280     doc : false,
45281     group: false,
45282     groupStack : false,
45283     hexStore : false,
45284     
45285     
45286     cpos : 0, 
45287     row : 1, // reportin?
45288     col : 1, //
45289
45290      
45291     push : function (el)
45292     {
45293         var m = 'cmd'+ el.type;
45294         if (typeof(this[m]) == 'undefined') {
45295             Roo.log('invalid cmd:' + el.type);
45296             return;
45297         }
45298         this[m](el);
45299         //Roo.log(el);
45300     },
45301     flushHexStore : function()
45302     {
45303         if (this.hexStore.length < 1) {
45304             return;
45305         }
45306         var hexstr = this.hexStore.map(
45307             function(cmd) {
45308                 return cmd.value;
45309         }).join('');
45310         
45311         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45312               
45313             
45314         this.hexStore.splice(0)
45315         
45316     },
45317     
45318     cmdgroupstart : function()
45319     {
45320         this.flushHexStore();
45321         if (this.group) {
45322             this.groupStack.push(this.group);
45323         }
45324          // parent..
45325         if (this.doc === false) {
45326             this.group = this.doc = new Roo.rtf.Document();
45327             return;
45328             
45329         }
45330         this.group = new Roo.rtf.Group(this.group);
45331     },
45332     cmdignorable : function()
45333     {
45334         this.flushHexStore();
45335         this.group.ignorable = true;
45336     },
45337     cmdendparagraph : function()
45338     {
45339         this.flushHexStore();
45340         this.group.addContent(new Roo.rtf.Paragraph());
45341     },
45342     cmdgroupend : function ()
45343     {
45344         this.flushHexStore();
45345         var endingGroup = this.group;
45346         
45347         
45348         this.group = this.groupStack.pop();
45349         if (this.group) {
45350             this.group.addChild(endingGroup);
45351         }
45352         
45353         
45354         
45355         var doc = this.group || this.doc;
45356         //if (endingGroup instanceof FontTable) {
45357         //  doc.fonts = endingGroup.table
45358         //} else if (endingGroup instanceof ColorTable) {
45359         //  doc.colors = endingGroup.table
45360         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45361         if (endingGroup.ignorable === false) {
45362             //code
45363             this.groups.push(endingGroup);
45364            // Roo.log( endingGroup );
45365         }
45366             //Roo.each(endingGroup.content, function(item)) {
45367             //    doc.addContent(item);
45368             //}
45369             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45370         //}
45371     },
45372     cmdtext : function (cmd)
45373     {
45374         this.flushHexStore();
45375         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45376             //this.group = this.doc
45377             return;  // we really don't care about stray text...
45378         }
45379         this.group.addContent(new Roo.rtf.Span(cmd));
45380     },
45381     cmdcontrolword : function (cmd)
45382     {
45383         this.flushHexStore();
45384         if (!this.group.type) {
45385             this.group.type = cmd.value;
45386             return;
45387         }
45388         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45389         // we actually don't care about ctrl words...
45390         return ;
45391         /*
45392         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45393         if (this[method]) {
45394             this[method](cmd.param)
45395         } else {
45396             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45397         }
45398         */
45399     },
45400     cmdhexchar : function(cmd) {
45401         this.hexStore.push(cmd);
45402     },
45403     cmderror : function(cmd) {
45404         throw cmd.value;
45405     },
45406     
45407     /*
45408       _flush (done) {
45409         if (this.text !== '\u0000') this.emitText()
45410         done()
45411       }
45412       */
45413       
45414       
45415     parseText : function(c)
45416     {
45417         if (c === '\\') {
45418             this.parserState = this.parseEscapes;
45419         } else if (c === '{') {
45420             this.emitStartGroup();
45421         } else if (c === '}') {
45422             this.emitEndGroup();
45423         } else if (c === '\x0A' || c === '\x0D') {
45424             // cr/lf are noise chars
45425         } else {
45426             this.text += c;
45427         }
45428     },
45429     
45430     parseEscapes: function (c)
45431     {
45432         if (c === '\\' || c === '{' || c === '}') {
45433             this.text += c;
45434             this.parserState = this.parseText;
45435         } else {
45436             this.parserState = this.parseControlSymbol;
45437             this.parseControlSymbol(c);
45438         }
45439     },
45440     parseControlSymbol: function(c)
45441     {
45442         if (c === '~') {
45443             this.text += '\u00a0'; // nbsp
45444             this.parserState = this.parseText
45445         } else if (c === '-') {
45446              this.text += '\u00ad'; // soft hyphen
45447         } else if (c === '_') {
45448             this.text += '\u2011'; // non-breaking hyphen
45449         } else if (c === '*') {
45450             this.emitIgnorable();
45451             this.parserState = this.parseText;
45452         } else if (c === "'") {
45453             this.parserState = this.parseHexChar;
45454         } else if (c === '|') { // formula cacter
45455             this.emitFormula();
45456             this.parserState = this.parseText;
45457         } else if (c === ':') { // subentry in an index entry
45458             this.emitIndexSubEntry();
45459             this.parserState = this.parseText;
45460         } else if (c === '\x0a') {
45461             this.emitEndParagraph();
45462             this.parserState = this.parseText;
45463         } else if (c === '\x0d') {
45464             this.emitEndParagraph();
45465             this.parserState = this.parseText;
45466         } else {
45467             this.parserState = this.parseControlWord;
45468             this.parseControlWord(c);
45469         }
45470     },
45471     parseHexChar: function (c)
45472     {
45473         if (/^[A-Fa-f0-9]$/.test(c)) {
45474             this.hexChar += c;
45475             if (this.hexChar.length >= 2) {
45476               this.emitHexChar();
45477               this.parserState = this.parseText;
45478             }
45479             return;
45480         }
45481         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45482         this.parserState = this.parseText;
45483         
45484     },
45485     parseControlWord : function(c)
45486     {
45487         if (c === ' ') {
45488             this.emitControlWord();
45489             this.parserState = this.parseText;
45490         } else if (/^[-\d]$/.test(c)) {
45491             this.parserState = this.parseControlWordParam;
45492             this.controlWordParam += c;
45493         } else if (/^[A-Za-z]$/.test(c)) {
45494           this.controlWord += c;
45495         } else {
45496           this.emitControlWord();
45497           this.parserState = this.parseText;
45498           this.parseText(c);
45499         }
45500     },
45501     parseControlWordParam : function (c) {
45502         if (/^\d$/.test(c)) {
45503           this.controlWordParam += c;
45504         } else if (c === ' ') {
45505           this.emitControlWord();
45506           this.parserState = this.parseText;
45507         } else {
45508           this.emitControlWord();
45509           this.parserState = this.parseText;
45510           this.parseText(c);
45511         }
45512     },
45513     
45514     
45515     
45516     
45517     emitText : function () {
45518         if (this.text === '') {
45519             return;
45520         }
45521         this.push({
45522             type: 'text',
45523             value: this.text,
45524             pos: this.cpos,
45525             row: this.row,
45526             col: this.col
45527         });
45528         this.text = ''
45529     },
45530     emitControlWord : function ()
45531     {
45532         this.emitText();
45533         if (this.controlWord === '') {
45534             // do we want to track this - it seems just to cause problems.
45535             //this.emitError('empty control word');
45536         } else {
45537             this.push({
45538                   type: 'controlword',
45539                   value: this.controlWord,
45540                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45541                   pos: this.cpos,
45542                   row: this.row,
45543                   col: this.col
45544             });
45545         }
45546         this.controlWord = '';
45547         this.controlWordParam = '';
45548     },
45549     emitStartGroup : function ()
45550     {
45551         this.emitText();
45552         this.push({
45553             type: 'groupstart',
45554             pos: this.cpos,
45555             row: this.row,
45556             col: this.col
45557         });
45558     },
45559     emitEndGroup : function ()
45560     {
45561         this.emitText();
45562         this.push({
45563             type: 'groupend',
45564             pos: this.cpos,
45565             row: this.row,
45566             col: this.col
45567         });
45568     },
45569     emitIgnorable : function ()
45570     {
45571         this.emitText();
45572         this.push({
45573             type: 'ignorable',
45574             pos: this.cpos,
45575             row: this.row,
45576             col: this.col
45577         });
45578     },
45579     emitHexChar : function ()
45580     {
45581         this.emitText();
45582         this.push({
45583             type: 'hexchar',
45584             value: this.hexChar,
45585             pos: this.cpos,
45586             row: this.row,
45587             col: this.col
45588         });
45589         this.hexChar = ''
45590     },
45591     emitError : function (message)
45592     {
45593       this.emitText();
45594       this.push({
45595             type: 'error',
45596             value: message,
45597             row: this.row,
45598             col: this.col,
45599             char: this.cpos //,
45600             //stack: new Error().stack
45601         });
45602     },
45603     emitEndParagraph : function () {
45604         this.emitText();
45605         this.push({
45606             type: 'endparagraph',
45607             pos: this.cpos,
45608             row: this.row,
45609             col: this.col
45610         });
45611     }
45612      
45613 } ;
45614 Roo.htmleditor = {};
45615  
45616 /**
45617  * @class Roo.htmleditor.Filter
45618  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45619  * @cfg {DomElement} node The node to iterate and filter
45620  * @cfg {boolean|String|Array} tag Tags to replace 
45621  * @constructor
45622  * Create a new Filter.
45623  * @param {Object} config Configuration options
45624  */
45625
45626
45627
45628 Roo.htmleditor.Filter = function(cfg) {
45629     Roo.apply(this.cfg);
45630     // this does not actually call walk as it's really just a abstract class
45631 }
45632
45633
45634 Roo.htmleditor.Filter.prototype = {
45635     
45636     node: false,
45637     
45638     tag: false,
45639
45640     // overrride to do replace comments.
45641     replaceComment : false,
45642     
45643     // overrride to do replace or do stuff with tags..
45644     replaceTag : false,
45645     
45646     walk : function(dom)
45647     {
45648         Roo.each( Array.from(dom.childNodes), function( e ) {
45649             switch(true) {
45650                 
45651                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
45652                     this.replaceComment(e);
45653                     return;
45654                 
45655                 case e.nodeType != 1: //not a node.
45656                     return;
45657                 
45658                 case this.tag === true: // everything
45659                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45660                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45661                     if (this.replaceTag && false === this.replaceTag(e)) {
45662                         return;
45663                     }
45664                     if (e.hasChildNodes()) {
45665                         this.walk(e);
45666                     }
45667                     return;
45668                 
45669                 default:    // tags .. that do not match.
45670                     if (e.hasChildNodes()) {
45671                         this.walk(e);
45672                     }
45673             }
45674             
45675         }, this);
45676         
45677     }
45678 }; 
45679
45680 /**
45681  * @class Roo.htmleditor.FilterAttributes
45682  * clean attributes and  styles including http:// etc.. in attribute
45683  * @constructor
45684 * Run a new Attribute Filter
45685 * @param {Object} config Configuration options
45686  */
45687 Roo.htmleditor.FilterAttributes = function(cfg)
45688 {
45689     Roo.apply(this, cfg);
45690     this.attrib_black = this.attrib_black || [];
45691     this.attrib_white = this.attrib_white || [];
45692
45693     this.attrib_clean = this.attrib_clean || [];
45694     this.style_white = this.style_white || [];
45695     this.style_black = this.style_black || [];
45696     this.walk(cfg.node);
45697 }
45698
45699 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45700 {
45701     tag: true, // all tags
45702     
45703     attrib_black : false, // array
45704     attrib_clean : false,
45705     attrib_white : false,
45706
45707     style_white : false,
45708     style_black : false,
45709      
45710      
45711     replaceTag : function(node)
45712     {
45713         if (!node.attributes || !node.attributes.length) {
45714             return true;
45715         }
45716         
45717         for (var i = node.attributes.length-1; i > -1 ; i--) {
45718             var a = node.attributes[i];
45719             //console.log(a);
45720             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45721                 node.removeAttribute(a.name);
45722                 continue;
45723             }
45724             
45725             
45726             
45727             if (a.name.toLowerCase().substr(0,2)=='on')  {
45728                 node.removeAttribute(a.name);
45729                 continue;
45730             }
45731             
45732             
45733             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45734                 node.removeAttribute(a.name);
45735                 continue;
45736             }
45737             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45738                 this.cleanAttr(node,a.name,a.value); // fixme..
45739                 continue;
45740             }
45741             if (a.name == 'style') {
45742                 this.cleanStyle(node,a.name,a.value);
45743                 continue;
45744             }
45745             /// clean up MS crap..
45746             // tecnically this should be a list of valid class'es..
45747             
45748             
45749             if (a.name == 'class') {
45750                 if (a.value.match(/^Mso/)) {
45751                     node.removeAttribute('class');
45752                 }
45753                 
45754                 if (a.value.match(/^body$/)) {
45755                     node.removeAttribute('class');
45756                 }
45757                 continue;
45758             }
45759             
45760             
45761             // style cleanup!?
45762             // class cleanup?
45763             
45764         }
45765         return true; // clean children
45766     },
45767         
45768     cleanAttr: function(node, n,v)
45769     {
45770         
45771         if (v.match(/^\./) || v.match(/^\//)) {
45772             return;
45773         }
45774         if (v.match(/^(http|https):\/\//)
45775             || v.match(/^mailto:/) 
45776             || v.match(/^ftp:/)
45777             || v.match(/^data:/)
45778             ) {
45779             return;
45780         }
45781         if (v.match(/^#/)) {
45782             return;
45783         }
45784         if (v.match(/^\{/)) { // allow template editing.
45785             return;
45786         }
45787 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45788         node.removeAttribute(n);
45789         
45790     },
45791     cleanStyle : function(node,  n,v)
45792     {
45793         if (v.match(/expression/)) { //XSS?? should we even bother..
45794             node.removeAttribute(n);
45795             return;
45796         }
45797         
45798         var parts = v.split(/;/);
45799         var clean = [];
45800         
45801         Roo.each(parts, function(p) {
45802             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45803             if (!p.length) {
45804                 return true;
45805             }
45806             var l = p.split(':').shift().replace(/\s+/g,'');
45807             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45808             
45809             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45810                 return true;
45811             }
45812             //Roo.log()
45813             // only allow 'c whitelisted system attributes'
45814             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45815                 return true;
45816             }
45817             
45818             
45819             clean.push(p);
45820             return true;
45821         },this);
45822         if (clean.length) { 
45823             node.setAttribute(n, clean.join(';'));
45824         } else {
45825             node.removeAttribute(n);
45826         }
45827         
45828     }
45829         
45830         
45831         
45832     
45833 });/**
45834  * @class Roo.htmleditor.FilterBlack
45835  * remove blacklisted elements.
45836  * @constructor
45837  * Run a new Blacklisted Filter
45838  * @param {Object} config Configuration options
45839  */
45840
45841 Roo.htmleditor.FilterBlack = function(cfg)
45842 {
45843     Roo.apply(this, cfg);
45844     this.walk(cfg.node);
45845 }
45846
45847 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45848 {
45849     tag : true, // all elements.
45850    
45851     replaceTag : function(n)
45852     {
45853         n.parentNode.removeChild(n);
45854     }
45855 });
45856 /**
45857  * @class Roo.htmleditor.FilterComment
45858  * remove comments.
45859  * @constructor
45860 * Run a new Comments Filter
45861 * @param {Object} config Configuration options
45862  */
45863 Roo.htmleditor.FilterComment = function(cfg)
45864 {
45865     this.walk(cfg.node);
45866 }
45867
45868 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45869 {
45870   
45871     replaceComment : function(n)
45872     {
45873         n.parentNode.removeChild(n);
45874     }
45875 });/**
45876  * @class Roo.htmleditor.FilterKeepChildren
45877  * remove tags but keep children
45878  * @constructor
45879  * Run a new Keep Children Filter
45880  * @param {Object} config Configuration options
45881  */
45882
45883 Roo.htmleditor.FilterKeepChildren = function(cfg)
45884 {
45885     Roo.apply(this, cfg);
45886     if (this.tag === false) {
45887         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45888     }
45889     this.walk(cfg.node);
45890 }
45891
45892 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45893 {
45894     
45895   
45896     replaceTag : function(node)
45897     {
45898         // walk children...
45899         //Roo.log(node);
45900         var ar = Array.from(node.childNodes);
45901         //remove first..
45902         for (var i = 0; i < ar.length; i++) {
45903             if (ar[i].nodeType == 1) {
45904                 if (
45905                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45906                     || // array and it matches
45907                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45908                 ) {
45909                     this.replaceTag(ar[i]); // child is blacklisted as well...
45910                     continue;
45911                 }
45912             }
45913         }  
45914         ar = Array.from(node.childNodes);
45915         for (var i = 0; i < ar.length; i++) {
45916          
45917             node.removeChild(ar[i]);
45918             // what if we need to walk these???
45919             node.parentNode.insertBefore(ar[i], node);
45920             if (this.tag !== false) {
45921                 this.walk(ar[i]);
45922                 
45923             }
45924         }
45925         node.parentNode.removeChild(node);
45926         return false; // don't walk children
45927         
45928         
45929     }
45930 });/**
45931  * @class Roo.htmleditor.FilterParagraph
45932  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45933  * like on 'push' to remove the <p> tags and replace them with line breaks.
45934  * @constructor
45935  * Run a new Paragraph Filter
45936  * @param {Object} config Configuration options
45937  */
45938
45939 Roo.htmleditor.FilterParagraph = function(cfg)
45940 {
45941     // no need to apply config.
45942     this.walk(cfg.node);
45943 }
45944
45945 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45946 {
45947     
45948      
45949     tag : 'P',
45950     
45951      
45952     replaceTag : function(node)
45953     {
45954         
45955         if (node.childNodes.length == 1 &&
45956             node.childNodes[0].nodeType == 3 &&
45957             node.childNodes[0].textContent.trim().length < 1
45958             ) {
45959             // remove and replace with '<BR>';
45960             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45961             return false; // no need to walk..
45962         }
45963         var ar = Array.from(node.childNodes);
45964         for (var i = 0; i < ar.length; i++) {
45965             node.removeChild(ar[i]);
45966             // what if we need to walk these???
45967             node.parentNode.insertBefore(ar[i], node);
45968         }
45969         // now what about this?
45970         // <p> &nbsp; </p>
45971         
45972         // double BR.
45973         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45974         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45975         node.parentNode.removeChild(node);
45976         
45977         return false;
45978
45979     }
45980     
45981 });/**
45982  * @class Roo.htmleditor.FilterSpan
45983  * filter span's with no attributes out..
45984  * @constructor
45985  * Run a new Span Filter
45986  * @param {Object} config Configuration options
45987  */
45988
45989 Roo.htmleditor.FilterSpan = function(cfg)
45990 {
45991     // no need to apply config.
45992     this.walk(cfg.node);
45993 }
45994
45995 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45996 {
45997      
45998     tag : 'SPAN',
45999      
46000  
46001     replaceTag : function(node)
46002     {
46003         if (node.attributes && node.attributes.length > 0) {
46004             return true; // walk if there are any.
46005         }
46006         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
46007         return false;
46008      
46009     }
46010     
46011 });/**
46012  * @class Roo.htmleditor.FilterTableWidth
46013   try and remove table width data - as that frequently messes up other stuff.
46014  * 
46015  *      was cleanTableWidths.
46016  *
46017  * Quite often pasting from word etc.. results in tables with column and widths.
46018  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
46019  *
46020  * @constructor
46021  * Run a new Table Filter
46022  * @param {Object} config Configuration options
46023  */
46024
46025 Roo.htmleditor.FilterTableWidth = function(cfg)
46026 {
46027     // no need to apply config.
46028     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46029     this.walk(cfg.node);
46030 }
46031
46032 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46033 {
46034      
46035      
46036     
46037     replaceTag: function(node) {
46038         
46039         
46040       
46041         if (node.hasAttribute('width')) {
46042             node.removeAttribute('width');
46043         }
46044         
46045          
46046         if (node.hasAttribute("style")) {
46047             // pretty basic...
46048             
46049             var styles = node.getAttribute("style").split(";");
46050             var nstyle = [];
46051             Roo.each(styles, function(s) {
46052                 if (!s.match(/:/)) {
46053                     return;
46054                 }
46055                 var kv = s.split(":");
46056                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46057                     return;
46058                 }
46059                 // what ever is left... we allow.
46060                 nstyle.push(s);
46061             });
46062             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46063             if (!nstyle.length) {
46064                 node.removeAttribute('style');
46065             }
46066         }
46067         
46068         return true; // continue doing children..
46069     }
46070 });/**
46071  * @class Roo.htmleditor.FilterWord
46072  * try and clean up all the mess that Word generates.
46073  * 
46074  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
46075  
46076  * @constructor
46077  * Run a new Span Filter
46078  * @param {Object} config Configuration options
46079  */
46080
46081 Roo.htmleditor.FilterWord = function(cfg)
46082 {
46083     // no need to apply config.
46084     this.replaceDocBullets(cfg.node);
46085     
46086    // this.walk(cfg.node);
46087     
46088     
46089 }
46090
46091 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46092 {
46093     tag: true,
46094      
46095     
46096     /**
46097      * Clean up MS wordisms...
46098      */
46099     replaceTag : function(node)
46100     {
46101          
46102         // no idea what this does - span with text, replaceds with just text.
46103         if(
46104                 node.nodeName == 'SPAN' &&
46105                 !node.hasAttributes() &&
46106                 node.childNodes.length == 1 &&
46107                 node.firstChild.nodeName == "#text"  
46108         ) {
46109             var textNode = node.firstChild;
46110             node.removeChild(textNode);
46111             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46112                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46113             }
46114             node.parentNode.insertBefore(textNode, node);
46115             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46116                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46117             }
46118             
46119             node.parentNode.removeChild(node);
46120             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46121         }
46122         
46123    
46124         
46125         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46126             node.parentNode.removeChild(node);
46127             return false; // dont do chidlren
46128         }
46129         //Roo.log(node.tagName);
46130         // remove - but keep children..
46131         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46132             //Roo.log('-- removed');
46133             while (node.childNodes.length) {
46134                 var cn = node.childNodes[0];
46135                 node.removeChild(cn);
46136                 node.parentNode.insertBefore(cn, node);
46137                 // move node to parent - and clean it..
46138                 if (cn.nodeType == 1) {
46139                     this.replaceTag(cn);
46140                 }
46141                 
46142             }
46143             node.parentNode.removeChild(node);
46144             /// no need to iterate chidlren = it's got none..
46145             //this.iterateChildren(node, this.cleanWord);
46146             return false; // no need to iterate children.
46147         }
46148         // clean styles
46149         if (node.className.length) {
46150             
46151             var cn = node.className.split(/\W+/);
46152             var cna = [];
46153             Roo.each(cn, function(cls) {
46154                 if (cls.match(/Mso[a-zA-Z]+/)) {
46155                     return;
46156                 }
46157                 cna.push(cls);
46158             });
46159             node.className = cna.length ? cna.join(' ') : '';
46160             if (!cna.length) {
46161                 node.removeAttribute("class");
46162             }
46163         }
46164         
46165         if (node.hasAttribute("lang")) {
46166             node.removeAttribute("lang");
46167         }
46168         
46169         if (node.hasAttribute("style")) {
46170             
46171             var styles = node.getAttribute("style").split(";");
46172             var nstyle = [];
46173             Roo.each(styles, function(s) {
46174                 if (!s.match(/:/)) {
46175                     return;
46176                 }
46177                 var kv = s.split(":");
46178                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46179                     return;
46180                 }
46181                 // what ever is left... we allow.
46182                 nstyle.push(s);
46183             });
46184             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46185             if (!nstyle.length) {
46186                 node.removeAttribute('style');
46187             }
46188         }
46189         return true; // do children
46190         
46191         
46192         
46193     },
46194     
46195     styleToObject: function(node)
46196     {
46197         var styles = (node.getAttribute("style") || '').split(";");
46198         var ret = {};
46199         Roo.each(styles, function(s) {
46200             if (!s.match(/:/)) {
46201                 return;
46202             }
46203             var kv = s.split(":");
46204              
46205             // what ever is left... we allow.
46206             ret[kv[0].trim()] = kv[1];
46207         });
46208         return ret;
46209     },
46210     
46211     
46212     replaceDocBullets : function(doc)
46213     {
46214         // this is a bit odd - but it appears some indents use ql-indent-1
46215         
46216         var listpara = doc.getElementsByClassName('ql-indent-1');
46217         while(listpara.length) {
46218             this.replaceDocBullet(listpara.item(0));
46219         }
46220         
46221         var listpara = doc.getElementsByClassName('MsoListParagraph');
46222         while(listpara.length) {
46223             this.replaceDocBullet(listpara.item(0));
46224         }
46225     },
46226     
46227     replaceDocBullet : function(p)
46228     {
46229         // gather all the siblings.
46230         var ns = p,
46231             parent = p.parentNode,
46232             doc = parent.ownerDocument,
46233             items = [];
46234             
46235             
46236         while (ns) {
46237             if (ns.nodeType != 1) {
46238                 ns = ns.nextSibling;
46239                 continue;
46240             }
46241             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
46242                 break;
46243             }
46244             items.push(ns);
46245             ns = ns.nextSibling;
46246         }
46247         
46248         
46249         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
46250         parent.insertBefore(ul, p);
46251         var lvl = 0;
46252         var stack = [ ul ];
46253         var last_li = false;
46254         
46255         items.forEach(function(n, ipos) {
46256             //Roo.log("got innertHMLT=" + n.innerHTML);
46257             
46258             var spans = n.getElementsByTagName('span');
46259             if (!spans.length) {
46260                 //Roo.log("No spans found");
46261
46262                 parent.removeChild(n);
46263                 return; // skip it...
46264             }
46265            
46266                 
46267             
46268             var style = {};
46269             for(var i = 0; i < spans.length; i++) {
46270             
46271                 style = this.styleToObject(spans[i]);
46272                 if (typeof(style['mso-list']) == 'undefined') {
46273                     continue;
46274                 }
46275                 
46276                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
46277                 break;
46278             }
46279             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
46280             style = this.styleToObject(n); // mo-list is from the parent node.
46281             if (typeof(style['mso-list']) == 'undefined') {
46282                 //Roo.log("parent is missing level");
46283                 parent.removeChild(n);
46284                 return;
46285             }
46286             
46287             var nlvl =   (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1  ;
46288             
46289             
46290             
46291             if (nlvl > lvl) {
46292                 //new indent
46293                 var nul = doc.createElement('ul'); // what about number lists...
46294                 last_li.appendChild(nul);
46295                 stack[nlvl] = nul;
46296                 
46297             }
46298             lvl = nlvl;
46299             
46300             var nli = stack[nlvl].appendChild(doc.createElement('li'));
46301             last_li = nli;
46302             nli.innerHTML = n.innerHTML;
46303             //Roo.log("innerHTML = " + n.innerHTML);
46304             parent.removeChild(n);
46305             
46306             // copy children of p into nli
46307             /*while(n.firstChild) {
46308                 var fc = n.firstChild;
46309                 n.removeChild(fc);
46310                 nli.appendChild(fc);
46311             }*/
46312              
46313             
46314         },this);
46315         
46316         
46317         
46318         
46319     }
46320     
46321     
46322     
46323 });
46324 /**
46325  * @class Roo.htmleditor.FilterStyleToTag
46326  * part of the word stuff... - certain 'styles' should be converted to tags.
46327  * eg.
46328  *   font-weight: bold -> bold
46329  *   ?? super / subscrit etc..
46330  * 
46331  * @constructor
46332 * Run a new style to tag filter.
46333 * @param {Object} config Configuration options
46334  */
46335 Roo.htmleditor.FilterStyleToTag = function(cfg)
46336 {
46337     
46338     this.tags = {
46339         B  : [ 'fontWeight' , 'bold'],
46340         I :  [ 'fontStyle' , 'italic'],
46341         //pre :  [ 'font-style' , 'italic'],
46342         // h1.. h6 ?? font-size?
46343         SUP : [ 'verticalAlign' , 'super' ],
46344         SUB : [ 'verticalAlign' , 'sub' ]
46345         
46346         
46347     };
46348     
46349     Roo.apply(this, cfg);
46350      
46351     
46352     this.walk(cfg.node);
46353     
46354     
46355     
46356 }
46357
46358
46359 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46360 {
46361     tag: true, // all tags
46362     
46363     tags : false,
46364     
46365     
46366     replaceTag : function(node)
46367     {
46368         
46369         
46370         if (node.getAttribute("style") === null) {
46371             return true;
46372         }
46373         var inject = [];
46374         for (var k in this.tags) {
46375             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46376                 inject.push(k);
46377                 node.style.removeProperty(this.tags[k][0]);
46378             }
46379         }
46380         if (!inject.length) {
46381             return true; 
46382         }
46383         var cn = Array.from(node.childNodes);
46384         var nn = node;
46385         Roo.each(inject, function(t) {
46386             var nc = node.ownerDocument.createElement(t);
46387             nn.appendChild(nc);
46388             nn = nc;
46389         });
46390         for(var i = 0;i < cn.length;cn++) {
46391             node.removeChild(cn[i]);
46392             nn.appendChild(cn[i]);
46393         }
46394         return true /// iterate thru
46395     }
46396     
46397 })/**
46398  * @class Roo.htmleditor.FilterLongBr
46399  * BR/BR/BR - keep a maximum of 2...
46400  * @constructor
46401  * Run a new Long BR Filter
46402  * @param {Object} config Configuration options
46403  */
46404
46405 Roo.htmleditor.FilterLongBr = function(cfg)
46406 {
46407     // no need to apply config.
46408     this.walk(cfg.node);
46409 }
46410
46411 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46412 {
46413     
46414      
46415     tag : 'BR',
46416     
46417      
46418     replaceTag : function(node)
46419     {
46420         
46421         var ps = node.nextSibling;
46422         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46423             ps = ps.nextSibling;
46424         }
46425         
46426         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46427             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46428             return false;
46429         }
46430         
46431         if (!ps || ps.nodeType != 1) {
46432             return false;
46433         }
46434         
46435         if (!ps || ps.tagName != 'BR') {
46436            
46437             return false;
46438         }
46439         
46440         
46441         
46442         
46443         
46444         if (!node.previousSibling) {
46445             return false;
46446         }
46447         var ps = node.previousSibling;
46448         
46449         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46450             ps = ps.previousSibling;
46451         }
46452         if (!ps || ps.nodeType != 1) {
46453             return false;
46454         }
46455         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46456         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46457             return false;
46458         }
46459         
46460         node.parentNode.removeChild(node); // remove me...
46461         
46462         return false; // no need to do children
46463
46464     }
46465     
46466 }); 
46467
46468 /**
46469  * @class Roo.htmleditor.FilterBlock
46470  * removes id / data-block and contenteditable that are associated with blocks
46471  * usage should be done on a cloned copy of the dom
46472  * @constructor
46473 * Run a new Attribute Filter { node : xxxx }}
46474 * @param {Object} config Configuration options
46475  */
46476 Roo.htmleditor.FilterBlock = function(cfg)
46477 {
46478     Roo.apply(this, cfg);
46479     var qa = cfg.node.querySelectorAll;
46480     this.removeAttributes('data-block');
46481     this.removeAttributes('contenteditable');
46482     this.removeAttributes('id');
46483     
46484 }
46485
46486 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46487 {
46488     node: true, // all tags
46489      
46490      
46491     removeAttributes : function(attr)
46492     {
46493         var ar = this.node.querySelectorAll('*[' + attr + ']');
46494         for (var i =0;i<ar.length;i++) {
46495             ar[i].removeAttribute(attr);
46496         }
46497     }
46498         
46499         
46500         
46501     
46502 });
46503 /***
46504  * This is based loosely on tinymce 
46505  * @class Roo.htmleditor.TidySerializer
46506  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46507  * @constructor
46508  * @method Serializer
46509  * @param {Object} settings Name/value settings object.
46510  */
46511
46512
46513 Roo.htmleditor.TidySerializer = function(settings)
46514 {
46515     Roo.apply(this, settings);
46516     
46517     this.writer = new Roo.htmleditor.TidyWriter(settings);
46518     
46519     
46520
46521 };
46522 Roo.htmleditor.TidySerializer.prototype = {
46523     
46524     /**
46525      * @param {boolean} inner do the inner of the node.
46526      */
46527     inner : false,
46528     
46529     writer : false,
46530     
46531     /**
46532     * Serializes the specified node into a string.
46533     *
46534     * @example
46535     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46536     * @method serialize
46537     * @param {DomElement} node Node instance to serialize.
46538     * @return {String} String with HTML based on DOM tree.
46539     */
46540     serialize : function(node) {
46541         
46542         // = settings.validate;
46543         var writer = this.writer;
46544         var self  = this;
46545         this.handlers = {
46546             // #text
46547             3: function(node) {
46548                 
46549                 writer.text(node.nodeValue, node);
46550             },
46551             // #comment
46552             8: function(node) {
46553                 writer.comment(node.nodeValue);
46554             },
46555             // Processing instruction
46556             7: function(node) {
46557                 writer.pi(node.name, node.nodeValue);
46558             },
46559             // Doctype
46560             10: function(node) {
46561                 writer.doctype(node.nodeValue);
46562             },
46563             // CDATA
46564             4: function(node) {
46565                 writer.cdata(node.nodeValue);
46566             },
46567             // Document fragment
46568             11: function(node) {
46569                 node = node.firstChild;
46570                 if (!node) {
46571                     return;
46572                 }
46573                 while(node) {
46574                     self.walk(node);
46575                     node = node.nextSibling
46576                 }
46577             }
46578         };
46579         writer.reset();
46580         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46581         return writer.getContent();
46582     },
46583
46584     walk: function(node)
46585     {
46586         var attrName, attrValue, sortedAttrs, i, l, elementRule,
46587             handler = this.handlers[node.nodeType];
46588             
46589         if (handler) {
46590             handler(node);
46591             return;
46592         }
46593     
46594         var name = node.nodeName;
46595         var isEmpty = node.childNodes.length < 1;
46596       
46597         var writer = this.writer;
46598         var attrs = node.attributes;
46599         // Sort attributes
46600         
46601         writer.start(node.nodeName, attrs, isEmpty, node);
46602         if (isEmpty) {
46603             return;
46604         }
46605         node = node.firstChild;
46606         if (!node) {
46607             writer.end(name);
46608             return;
46609         }
46610         while (node) {
46611             this.walk(node);
46612             node = node.nextSibling;
46613         }
46614         writer.end(name);
46615         
46616     
46617     }
46618     // Serialize element and treat all non elements as fragments
46619    
46620 }; 
46621
46622 /***
46623  * This is based loosely on tinymce 
46624  * @class Roo.htmleditor.TidyWriter
46625  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46626  *
46627  * Known issues?
46628  * - not tested much with 'PRE' formated elements.
46629  * 
46630  *
46631  *
46632  */
46633
46634 Roo.htmleditor.TidyWriter = function(settings)
46635 {
46636     
46637     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46638     Roo.apply(this, settings);
46639     this.html = [];
46640     this.state = [];
46641      
46642     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46643   
46644 }
46645 Roo.htmleditor.TidyWriter.prototype = {
46646
46647  
46648     state : false,
46649     
46650     indent :  '  ',
46651     
46652     // part of state...
46653     indentstr : '',
46654     in_pre: false,
46655     in_inline : false,
46656     last_inline : false,
46657     encode : false,
46658      
46659     
46660             /**
46661     * Writes the a start element such as <p id="a">.
46662     *
46663     * @method start
46664     * @param {String} name Name of the element.
46665     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46666     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46667     */
46668     start: function(name, attrs, empty, node)
46669     {
46670         var i, l, attr, value;
46671         
46672         // there are some situations where adding line break && indentation will not work. will not work.
46673         // <span / b / i ... formating?
46674         
46675         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46676         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46677         
46678         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46679         
46680         var add_lb = name == 'BR' ? false : in_inline;
46681         
46682         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46683             i_inline = false;
46684         }
46685
46686         var indentstr =  this.indentstr;
46687         
46688         // e_inline = elements that can be inline, but still allow \n before and after?
46689         // only 'BR' ??? any others?
46690         
46691         // ADD LINE BEFORE tage
46692         if (!this.in_pre) {
46693             if (in_inline) {
46694                 //code
46695                 if (name == 'BR') {
46696                     this.addLine();
46697                 } else if (this.lastElementEndsWS()) {
46698                     this.addLine();
46699                 } else{
46700                     // otherwise - no new line. (and dont indent.)
46701                     indentstr = '';
46702                 }
46703                 
46704             } else {
46705                 this.addLine();
46706             }
46707         } else {
46708             indentstr = '';
46709         }
46710         
46711         this.html.push(indentstr + '<', name.toLowerCase());
46712         
46713         if (attrs) {
46714             for (i = 0, l = attrs.length; i < l; i++) {
46715                 attr = attrs[i];
46716                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46717             }
46718         }
46719      
46720         if (empty) {
46721             if (is_short) {
46722                 this.html[this.html.length] = '/>';
46723             } else {
46724                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46725             }
46726             var e_inline = name == 'BR' ? false : this.in_inline;
46727             
46728             if (!e_inline && !this.in_pre) {
46729                 this.addLine();
46730             }
46731             return;
46732         
46733         }
46734         // not empty..
46735         this.html[this.html.length] = '>';
46736         
46737         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46738         /*
46739         if (!in_inline && !in_pre) {
46740             var cn = node.firstChild;
46741             while(cn) {
46742                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46743                     in_inline = true
46744                     break;
46745                 }
46746                 cn = cn.nextSibling;
46747             }
46748              
46749         }
46750         */
46751         
46752         
46753         this.pushState({
46754             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
46755             in_pre : in_pre,
46756             in_inline :  in_inline
46757         });
46758         // add a line after if we are not in a
46759         
46760         if (!in_inline && !in_pre) {
46761             this.addLine();
46762         }
46763         
46764             
46765          
46766         
46767     },
46768     
46769     lastElementEndsWS : function()
46770     {
46771         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46772         if (value === false) {
46773             return true;
46774         }
46775         return value.match(/\s+$/);
46776         
46777     },
46778     
46779     /**
46780      * Writes the a end element such as </p>.
46781      *
46782      * @method end
46783      * @param {String} name Name of the element.
46784      */
46785     end: function(name) {
46786         var value;
46787         this.popState();
46788         var indentstr = '';
46789         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46790         
46791         if (!this.in_pre && !in_inline) {
46792             this.addLine();
46793             indentstr  = this.indentstr;
46794         }
46795         this.html.push(indentstr + '</', name.toLowerCase(), '>');
46796         this.last_inline = in_inline;
46797         
46798         // pop the indent state..
46799     },
46800     /**
46801      * Writes a text node.
46802      *
46803      * In pre - we should not mess with the contents.
46804      * 
46805      *
46806      * @method text
46807      * @param {String} text String to write out.
46808      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46809      */
46810     text: function(in_text, node)
46811     {
46812         // if not in whitespace critical
46813         if (in_text.length < 1) {
46814             return;
46815         }
46816         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
46817         
46818         if (this.in_pre) {
46819             this.html[this.html.length] =  text;
46820             return;   
46821         }
46822         
46823         if (this.in_inline) {
46824             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
46825             if (text != ' ') {
46826                 text = text.replace(/\s+/,' ');  // all white space to single white space
46827                 
46828                     
46829                 // if next tag is '<BR>', then we can trim right..
46830                 if (node.nextSibling &&
46831                     node.nextSibling.nodeType == 1 &&
46832                     node.nextSibling.nodeName == 'BR' )
46833                 {
46834                     text = text.replace(/\s+$/g,'');
46835                 }
46836                 // if previous tag was a BR, we can also trim..
46837                 if (node.previousSibling &&
46838                     node.previousSibling.nodeType == 1 &&
46839                     node.previousSibling.nodeName == 'BR' )
46840                 {
46841                     text = this.indentstr +  text.replace(/^\s+/g,'');
46842                 }
46843                 if (text.match(/\n/)) {
46844                     text = text.replace(
46845                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46846                     );
46847                     // remoeve the last whitespace / line break.
46848                     text = text.replace(/\n\s+$/,'');
46849                 }
46850                 // repace long lines
46851                 
46852             }
46853              
46854             this.html[this.html.length] =  text;
46855             return;   
46856         }
46857         // see if previous element was a inline element.
46858         var indentstr = this.indentstr;
46859    
46860         text = text.replace(/\s+/g," "); // all whitespace into single white space.
46861         
46862         // should trim left?
46863         if (node.previousSibling &&
46864             node.previousSibling.nodeType == 1 &&
46865             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
46866         {
46867             indentstr = '';
46868             
46869         } else {
46870             this.addLine();
46871             text = text.replace(/^\s+/,''); // trim left
46872           
46873         }
46874         // should trim right?
46875         if (node.nextSibling &&
46876             node.nextSibling.nodeType == 1 &&
46877             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
46878         {
46879           // noop
46880             
46881         }  else {
46882             text = text.replace(/\s+$/,''); // trim right
46883         }
46884          
46885               
46886         
46887         
46888         
46889         if (text.length < 1) {
46890             return;
46891         }
46892         if (!text.match(/\n/)) {
46893             this.html.push(indentstr + text);
46894             return;
46895         }
46896         
46897         text = this.indentstr + text.replace(
46898             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46899         );
46900         // remoeve the last whitespace / line break.
46901         text = text.replace(/\s+$/,''); 
46902         
46903         this.html.push(text);
46904         
46905         // split and indent..
46906         
46907         
46908     },
46909     /**
46910      * Writes a cdata node such as <![CDATA[data]]>.
46911      *
46912      * @method cdata
46913      * @param {String} text String to write out inside the cdata.
46914      */
46915     cdata: function(text) {
46916         this.html.push('<![CDATA[', text, ']]>');
46917     },
46918     /**
46919     * Writes a comment node such as <!-- Comment -->.
46920     *
46921     * @method cdata
46922     * @param {String} text String to write out inside the comment.
46923     */
46924    comment: function(text) {
46925        this.html.push('<!--', text, '-->');
46926    },
46927     /**
46928      * Writes a PI node such as <?xml attr="value" ?>.
46929      *
46930      * @method pi
46931      * @param {String} name Name of the pi.
46932      * @param {String} text String to write out inside the pi.
46933      */
46934     pi: function(name, text) {
46935         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
46936         this.indent != '' && this.html.push('\n');
46937     },
46938     /**
46939      * Writes a doctype node such as <!DOCTYPE data>.
46940      *
46941      * @method doctype
46942      * @param {String} text String to write out inside the doctype.
46943      */
46944     doctype: function(text) {
46945         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
46946     },
46947     /**
46948      * Resets the internal buffer if one wants to reuse the writer.
46949      *
46950      * @method reset
46951      */
46952     reset: function() {
46953         this.html.length = 0;
46954         this.state = [];
46955         this.pushState({
46956             indentstr : '',
46957             in_pre : false, 
46958             in_inline : false
46959         })
46960     },
46961     /**
46962      * Returns the contents that got serialized.
46963      *
46964      * @method getContent
46965      * @return {String} HTML contents that got written down.
46966      */
46967     getContent: function() {
46968         return this.html.join('').replace(/\n$/, '');
46969     },
46970     
46971     pushState : function(cfg)
46972     {
46973         this.state.push(cfg);
46974         Roo.apply(this, cfg);
46975     },
46976     
46977     popState : function()
46978     {
46979         if (this.state.length < 1) {
46980             return; // nothing to push
46981         }
46982         var cfg = {
46983             in_pre: false,
46984             indentstr : ''
46985         };
46986         this.state.pop();
46987         if (this.state.length > 0) {
46988             cfg = this.state[this.state.length-1]; 
46989         }
46990         Roo.apply(this, cfg);
46991     },
46992     
46993     addLine: function()
46994     {
46995         if (this.html.length < 1) {
46996             return;
46997         }
46998         
46999         
47000         var value = this.html[this.html.length - 1];
47001         if (value.length > 0 && '\n' !== value) {
47002             this.html.push('\n');
47003         }
47004     }
47005     
47006     
47007 //'pre script noscript style textarea video audio iframe object code'
47008 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
47009 // inline 
47010 };
47011
47012 Roo.htmleditor.TidyWriter.inline_elements = [
47013         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
47014         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
47015 ];
47016 Roo.htmleditor.TidyWriter.shortend_elements = [
47017     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
47018     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
47019 ];
47020
47021 Roo.htmleditor.TidyWriter.whitespace_elements = [
47022     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
47023 ];/***
47024  * This is based loosely on tinymce 
47025  * @class Roo.htmleditor.TidyEntities
47026  * @static
47027  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47028  *
47029  * Not 100% sure this is actually used or needed.
47030  */
47031
47032 Roo.htmleditor.TidyEntities = {
47033     
47034     /**
47035      * initialize data..
47036      */
47037     init : function (){
47038      
47039         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
47040        
47041     },
47042
47043
47044     buildEntitiesLookup: function(items, radix) {
47045         var i, chr, entity, lookup = {};
47046         if (!items) {
47047             return {};
47048         }
47049         items = typeof(items) == 'string' ? items.split(',') : items;
47050         radix = radix || 10;
47051         // Build entities lookup table
47052         for (i = 0; i < items.length; i += 2) {
47053             chr = String.fromCharCode(parseInt(items[i], radix));
47054             // Only add non base entities
47055             if (!this.baseEntities[chr]) {
47056                 entity = '&' + items[i + 1] + ';';
47057                 lookup[chr] = entity;
47058                 lookup[entity] = chr;
47059             }
47060         }
47061         return lookup;
47062         
47063     },
47064     
47065     asciiMap : {
47066             128: '€',
47067             130: '‚',
47068             131: 'ƒ',
47069             132: '„',
47070             133: '…',
47071             134: '†',
47072             135: '‡',
47073             136: 'ˆ',
47074             137: '‰',
47075             138: 'Š',
47076             139: '‹',
47077             140: 'Œ',
47078             142: 'Ž',
47079             145: '‘',
47080             146: '’',
47081             147: '“',
47082             148: '”',
47083             149: '•',
47084             150: '–',
47085             151: '—',
47086             152: '˜',
47087             153: '™',
47088             154: 'š',
47089             155: '›',
47090             156: 'œ',
47091             158: 'ž',
47092             159: 'Ÿ'
47093     },
47094     // Raw entities
47095     baseEntities : {
47096         '"': '&quot;',
47097         // Needs to be escaped since the YUI compressor would otherwise break the code
47098         '\'': '&#39;',
47099         '<': '&lt;',
47100         '>': '&gt;',
47101         '&': '&amp;',
47102         '`': '&#96;'
47103     },
47104     // Reverse lookup table for raw entities
47105     reverseEntities : {
47106         '&lt;': '<',
47107         '&gt;': '>',
47108         '&amp;': '&',
47109         '&quot;': '"',
47110         '&apos;': '\''
47111     },
47112     
47113     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47114     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47115     rawCharsRegExp : /[<>&\"\']/g,
47116     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
47117     namedEntities  : false,
47118     namedEntitiesData : [ 
47119         '50',
47120         'nbsp',
47121         '51',
47122         'iexcl',
47123         '52',
47124         'cent',
47125         '53',
47126         'pound',
47127         '54',
47128         'curren',
47129         '55',
47130         'yen',
47131         '56',
47132         'brvbar',
47133         '57',
47134         'sect',
47135         '58',
47136         'uml',
47137         '59',
47138         'copy',
47139         '5a',
47140         'ordf',
47141         '5b',
47142         'laquo',
47143         '5c',
47144         'not',
47145         '5d',
47146         'shy',
47147         '5e',
47148         'reg',
47149         '5f',
47150         'macr',
47151         '5g',
47152         'deg',
47153         '5h',
47154         'plusmn',
47155         '5i',
47156         'sup2',
47157         '5j',
47158         'sup3',
47159         '5k',
47160         'acute',
47161         '5l',
47162         'micro',
47163         '5m',
47164         'para',
47165         '5n',
47166         'middot',
47167         '5o',
47168         'cedil',
47169         '5p',
47170         'sup1',
47171         '5q',
47172         'ordm',
47173         '5r',
47174         'raquo',
47175         '5s',
47176         'frac14',
47177         '5t',
47178         'frac12',
47179         '5u',
47180         'frac34',
47181         '5v',
47182         'iquest',
47183         '60',
47184         'Agrave',
47185         '61',
47186         'Aacute',
47187         '62',
47188         'Acirc',
47189         '63',
47190         'Atilde',
47191         '64',
47192         'Auml',
47193         '65',
47194         'Aring',
47195         '66',
47196         'AElig',
47197         '67',
47198         'Ccedil',
47199         '68',
47200         'Egrave',
47201         '69',
47202         'Eacute',
47203         '6a',
47204         'Ecirc',
47205         '6b',
47206         'Euml',
47207         '6c',
47208         'Igrave',
47209         '6d',
47210         'Iacute',
47211         '6e',
47212         'Icirc',
47213         '6f',
47214         'Iuml',
47215         '6g',
47216         'ETH',
47217         '6h',
47218         'Ntilde',
47219         '6i',
47220         'Ograve',
47221         '6j',
47222         'Oacute',
47223         '6k',
47224         'Ocirc',
47225         '6l',
47226         'Otilde',
47227         '6m',
47228         'Ouml',
47229         '6n',
47230         'times',
47231         '6o',
47232         'Oslash',
47233         '6p',
47234         'Ugrave',
47235         '6q',
47236         'Uacute',
47237         '6r',
47238         'Ucirc',
47239         '6s',
47240         'Uuml',
47241         '6t',
47242         'Yacute',
47243         '6u',
47244         'THORN',
47245         '6v',
47246         'szlig',
47247         '70',
47248         'agrave',
47249         '71',
47250         'aacute',
47251         '72',
47252         'acirc',
47253         '73',
47254         'atilde',
47255         '74',
47256         'auml',
47257         '75',
47258         'aring',
47259         '76',
47260         'aelig',
47261         '77',
47262         'ccedil',
47263         '78',
47264         'egrave',
47265         '79',
47266         'eacute',
47267         '7a',
47268         'ecirc',
47269         '7b',
47270         'euml',
47271         '7c',
47272         'igrave',
47273         '7d',
47274         'iacute',
47275         '7e',
47276         'icirc',
47277         '7f',
47278         'iuml',
47279         '7g',
47280         'eth',
47281         '7h',
47282         'ntilde',
47283         '7i',
47284         'ograve',
47285         '7j',
47286         'oacute',
47287         '7k',
47288         'ocirc',
47289         '7l',
47290         'otilde',
47291         '7m',
47292         'ouml',
47293         '7n',
47294         'divide',
47295         '7o',
47296         'oslash',
47297         '7p',
47298         'ugrave',
47299         '7q',
47300         'uacute',
47301         '7r',
47302         'ucirc',
47303         '7s',
47304         'uuml',
47305         '7t',
47306         'yacute',
47307         '7u',
47308         'thorn',
47309         '7v',
47310         'yuml',
47311         'ci',
47312         'fnof',
47313         'sh',
47314         'Alpha',
47315         'si',
47316         'Beta',
47317         'sj',
47318         'Gamma',
47319         'sk',
47320         'Delta',
47321         'sl',
47322         'Epsilon',
47323         'sm',
47324         'Zeta',
47325         'sn',
47326         'Eta',
47327         'so',
47328         'Theta',
47329         'sp',
47330         'Iota',
47331         'sq',
47332         'Kappa',
47333         'sr',
47334         'Lambda',
47335         'ss',
47336         'Mu',
47337         'st',
47338         'Nu',
47339         'su',
47340         'Xi',
47341         'sv',
47342         'Omicron',
47343         't0',
47344         'Pi',
47345         't1',
47346         'Rho',
47347         't3',
47348         'Sigma',
47349         't4',
47350         'Tau',
47351         't5',
47352         'Upsilon',
47353         't6',
47354         'Phi',
47355         't7',
47356         'Chi',
47357         't8',
47358         'Psi',
47359         't9',
47360         'Omega',
47361         'th',
47362         'alpha',
47363         'ti',
47364         'beta',
47365         'tj',
47366         'gamma',
47367         'tk',
47368         'delta',
47369         'tl',
47370         'epsilon',
47371         'tm',
47372         'zeta',
47373         'tn',
47374         'eta',
47375         'to',
47376         'theta',
47377         'tp',
47378         'iota',
47379         'tq',
47380         'kappa',
47381         'tr',
47382         'lambda',
47383         'ts',
47384         'mu',
47385         'tt',
47386         'nu',
47387         'tu',
47388         'xi',
47389         'tv',
47390         'omicron',
47391         'u0',
47392         'pi',
47393         'u1',
47394         'rho',
47395         'u2',
47396         'sigmaf',
47397         'u3',
47398         'sigma',
47399         'u4',
47400         'tau',
47401         'u5',
47402         'upsilon',
47403         'u6',
47404         'phi',
47405         'u7',
47406         'chi',
47407         'u8',
47408         'psi',
47409         'u9',
47410         'omega',
47411         'uh',
47412         'thetasym',
47413         'ui',
47414         'upsih',
47415         'um',
47416         'piv',
47417         '812',
47418         'bull',
47419         '816',
47420         'hellip',
47421         '81i',
47422         'prime',
47423         '81j',
47424         'Prime',
47425         '81u',
47426         'oline',
47427         '824',
47428         'frasl',
47429         '88o',
47430         'weierp',
47431         '88h',
47432         'image',
47433         '88s',
47434         'real',
47435         '892',
47436         'trade',
47437         '89l',
47438         'alefsym',
47439         '8cg',
47440         'larr',
47441         '8ch',
47442         'uarr',
47443         '8ci',
47444         'rarr',
47445         '8cj',
47446         'darr',
47447         '8ck',
47448         'harr',
47449         '8dl',
47450         'crarr',
47451         '8eg',
47452         'lArr',
47453         '8eh',
47454         'uArr',
47455         '8ei',
47456         'rArr',
47457         '8ej',
47458         'dArr',
47459         '8ek',
47460         'hArr',
47461         '8g0',
47462         'forall',
47463         '8g2',
47464         'part',
47465         '8g3',
47466         'exist',
47467         '8g5',
47468         'empty',
47469         '8g7',
47470         'nabla',
47471         '8g8',
47472         'isin',
47473         '8g9',
47474         'notin',
47475         '8gb',
47476         'ni',
47477         '8gf',
47478         'prod',
47479         '8gh',
47480         'sum',
47481         '8gi',
47482         'minus',
47483         '8gn',
47484         'lowast',
47485         '8gq',
47486         'radic',
47487         '8gt',
47488         'prop',
47489         '8gu',
47490         'infin',
47491         '8h0',
47492         'ang',
47493         '8h7',
47494         'and',
47495         '8h8',
47496         'or',
47497         '8h9',
47498         'cap',
47499         '8ha',
47500         'cup',
47501         '8hb',
47502         'int',
47503         '8hk',
47504         'there4',
47505         '8hs',
47506         'sim',
47507         '8i5',
47508         'cong',
47509         '8i8',
47510         'asymp',
47511         '8j0',
47512         'ne',
47513         '8j1',
47514         'equiv',
47515         '8j4',
47516         'le',
47517         '8j5',
47518         'ge',
47519         '8k2',
47520         'sub',
47521         '8k3',
47522         'sup',
47523         '8k4',
47524         'nsub',
47525         '8k6',
47526         'sube',
47527         '8k7',
47528         'supe',
47529         '8kl',
47530         'oplus',
47531         '8kn',
47532         'otimes',
47533         '8l5',
47534         'perp',
47535         '8m5',
47536         'sdot',
47537         '8o8',
47538         'lceil',
47539         '8o9',
47540         'rceil',
47541         '8oa',
47542         'lfloor',
47543         '8ob',
47544         'rfloor',
47545         '8p9',
47546         'lang',
47547         '8pa',
47548         'rang',
47549         '9ea',
47550         'loz',
47551         '9j0',
47552         'spades',
47553         '9j3',
47554         'clubs',
47555         '9j5',
47556         'hearts',
47557         '9j6',
47558         'diams',
47559         'ai',
47560         'OElig',
47561         'aj',
47562         'oelig',
47563         'b0',
47564         'Scaron',
47565         'b1',
47566         'scaron',
47567         'bo',
47568         'Yuml',
47569         'm6',
47570         'circ',
47571         'ms',
47572         'tilde',
47573         '802',
47574         'ensp',
47575         '803',
47576         'emsp',
47577         '809',
47578         'thinsp',
47579         '80c',
47580         'zwnj',
47581         '80d',
47582         'zwj',
47583         '80e',
47584         'lrm',
47585         '80f',
47586         'rlm',
47587         '80j',
47588         'ndash',
47589         '80k',
47590         'mdash',
47591         '80o',
47592         'lsquo',
47593         '80p',
47594         'rsquo',
47595         '80q',
47596         'sbquo',
47597         '80s',
47598         'ldquo',
47599         '80t',
47600         'rdquo',
47601         '80u',
47602         'bdquo',
47603         '810',
47604         'dagger',
47605         '811',
47606         'Dagger',
47607         '81g',
47608         'permil',
47609         '81p',
47610         'lsaquo',
47611         '81q',
47612         'rsaquo',
47613         '85c',
47614         'euro'
47615     ],
47616
47617          
47618     /**
47619      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47620      *
47621      * @method encodeRaw
47622      * @param {String} text Text to encode.
47623      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47624      * @return {String} Entity encoded text.
47625      */
47626     encodeRaw: function(text, attr)
47627     {
47628         var t = this;
47629         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47630             return t.baseEntities[chr] || chr;
47631         });
47632     },
47633     /**
47634      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47635      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47636      * and is exposed as the DOMUtils.encode function.
47637      *
47638      * @method encodeAllRaw
47639      * @param {String} text Text to encode.
47640      * @return {String} Entity encoded text.
47641      */
47642     encodeAllRaw: function(text) {
47643         var t = this;
47644         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47645             return t.baseEntities[chr] || chr;
47646         });
47647     },
47648     /**
47649      * Encodes the specified string using numeric entities. The core entities will be
47650      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47651      *
47652      * @method encodeNumeric
47653      * @param {String} text Text to encode.
47654      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47655      * @return {String} Entity encoded text.
47656      */
47657     encodeNumeric: function(text, attr) {
47658         var t = this;
47659         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47660             // Multi byte sequence convert it to a single entity
47661             if (chr.length > 1) {
47662                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47663             }
47664             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47665         });
47666     },
47667     /**
47668      * Encodes the specified string using named entities. The core entities will be encoded
47669      * as named ones but all non lower ascii characters will be encoded into named entities.
47670      *
47671      * @method encodeNamed
47672      * @param {String} text Text to encode.
47673      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47674      * @param {Object} entities Optional parameter with entities to use.
47675      * @return {String} Entity encoded text.
47676      */
47677     encodeNamed: function(text, attr, entities) {
47678         var t = this;
47679         entities = entities || this.namedEntities;
47680         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47681             return t.baseEntities[chr] || entities[chr] || chr;
47682         });
47683     },
47684     /**
47685      * Returns an encode function based on the name(s) and it's optional entities.
47686      *
47687      * @method getEncodeFunc
47688      * @param {String} name Comma separated list of encoders for example named,numeric.
47689      * @param {String} entities Optional parameter with entities to use instead of the built in set.
47690      * @return {function} Encode function to be used.
47691      */
47692     getEncodeFunc: function(name, entities) {
47693         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47694         var t = this;
47695         function encodeNamedAndNumeric(text, attr) {
47696             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47697                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47698             });
47699         }
47700
47701         function encodeCustomNamed(text, attr) {
47702             return t.encodeNamed(text, attr, entities);
47703         }
47704         // Replace + with , to be compatible with previous TinyMCE versions
47705         name = this.makeMap(name.replace(/\+/g, ','));
47706         // Named and numeric encoder
47707         if (name.named && name.numeric) {
47708             return this.encodeNamedAndNumeric;
47709         }
47710         // Named encoder
47711         if (name.named) {
47712             // Custom names
47713             if (entities) {
47714                 return encodeCustomNamed;
47715             }
47716             return this.encodeNamed;
47717         }
47718         // Numeric
47719         if (name.numeric) {
47720             return this.encodeNumeric;
47721         }
47722         // Raw encoder
47723         return this.encodeRaw;
47724     },
47725     /**
47726      * Decodes the specified string, this will replace entities with raw UTF characters.
47727      *
47728      * @method decode
47729      * @param {String} text Text to entity decode.
47730      * @return {String} Entity decoded string.
47731      */
47732     decode: function(text)
47733     {
47734         var  t = this;
47735         return text.replace(this.entityRegExp, function(all, numeric) {
47736             if (numeric) {
47737                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47738                 // Support upper UTF
47739                 if (numeric > 65535) {
47740                     numeric -= 65536;
47741                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47742                 }
47743                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47744             }
47745             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47746         });
47747     },
47748     nativeDecode : function (text) {
47749         return text;
47750     },
47751     makeMap : function (items, delim, map) {
47752                 var i;
47753                 items = items || [];
47754                 delim = delim || ',';
47755                 if (typeof items == "string") {
47756                         items = items.split(delim);
47757                 }
47758                 map = map || {};
47759                 i = items.length;
47760                 while (i--) {
47761                         map[items[i]] = {};
47762                 }
47763                 return map;
47764         }
47765 };
47766     
47767     
47768     
47769 Roo.htmleditor.TidyEntities.init();
47770 /**
47771  * @class Roo.htmleditor.KeyEnter
47772  * Handle Enter press..
47773  * @cfg {Roo.HtmlEditorCore} core the editor.
47774  * @constructor
47775  * Create a new Filter.
47776  * @param {Object} config Configuration options
47777  */
47778
47779
47780
47781
47782
47783 Roo.htmleditor.KeyEnter = function(cfg) {
47784     Roo.apply(this, cfg);
47785     // this does not actually call walk as it's really just a abstract class
47786  
47787     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47788 }
47789
47790 //Roo.htmleditor.KeyEnter.i = 0;
47791
47792
47793 Roo.htmleditor.KeyEnter.prototype = {
47794     
47795     core : false,
47796     
47797     keypress : function(e)
47798     {
47799         if (e.charCode != 13 && e.charCode != 10) {
47800             Roo.log([e.charCode,e]);
47801             return true;
47802         }
47803         e.preventDefault();
47804         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47805         var doc = this.core.doc;
47806           //add a new line
47807        
47808     
47809         var sel = this.core.getSelection();
47810         var range = sel.getRangeAt(0);
47811         var n = range.commonAncestorContainer;
47812         var pc = range.closest([ 'ol', 'ul']);
47813         var pli = range.closest('li');
47814         if (!pc || e.ctrlKey) {
47815             sel.insertNode('br', 'after'); 
47816          
47817             this.core.undoManager.addEvent();
47818             this.core.fireEditorEvent(e);
47819             return false;
47820         }
47821         
47822         // deal with <li> insetion
47823         if (pli.innerText.trim() == '' &&
47824             pli.previousSibling &&
47825             pli.previousSibling.nodeName == 'LI' &&
47826             pli.previousSibling.innerText.trim() ==  '') {
47827             pli.parentNode.removeChild(pli.previousSibling);
47828             sel.cursorAfter(pc);
47829             this.core.undoManager.addEvent();
47830             this.core.fireEditorEvent(e);
47831             return false;
47832         }
47833     
47834         var li = doc.createElement('LI');
47835         li.innerHTML = '&nbsp;';
47836         if (!pli || !pli.firstSibling) {
47837             pc.appendChild(li);
47838         } else {
47839             pli.parentNode.insertBefore(li, pli.firstSibling);
47840         }
47841         sel.cursorText (li.firstChild);
47842       
47843         this.core.undoManager.addEvent();
47844         this.core.fireEditorEvent(e);
47845
47846         return false;
47847         
47848     
47849         
47850         
47851          
47852     }
47853 };
47854      
47855 /**
47856  * @class Roo.htmleditor.Block
47857  * Base class for html editor blocks - do not use it directly .. extend it..
47858  * @cfg {DomElement} node The node to apply stuff to.
47859  * @cfg {String} friendly_name the name that appears in the context bar about this block
47860  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
47861  
47862  * @constructor
47863  * Create a new Filter.
47864  * @param {Object} config Configuration options
47865  */
47866
47867 Roo.htmleditor.Block  = function(cfg)
47868 {
47869     // do nothing .. should not be called really.
47870 }
47871 /**
47872  * factory method to get the block from an element (using cache if necessary)
47873  * @static
47874  * @param {HtmlElement} the dom element
47875  */
47876 Roo.htmleditor.Block.factory = function(node)
47877 {
47878     var cc = Roo.htmleditor.Block.cache;
47879     var id = Roo.get(node).id;
47880     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
47881         Roo.htmleditor.Block.cache[id].readElement(node);
47882         return Roo.htmleditor.Block.cache[id];
47883     }
47884     var db  = node.getAttribute('data-block');
47885     if (!db) {
47886         db = node.nodeName.toLowerCase().toUpperCaseFirst();
47887     }
47888     var cls = Roo.htmleditor['Block' + db];
47889     if (typeof(cls) == 'undefined') {
47890         //Roo.log(node.getAttribute('data-block'));
47891         Roo.log("OOps missing block : " + 'Block' + db);
47892         return false;
47893     }
47894     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
47895     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
47896 };
47897
47898 /**
47899  * initalize all Elements from content that are 'blockable'
47900  * @static
47901  * @param the body element
47902  */
47903 Roo.htmleditor.Block.initAll = function(body, type)
47904 {
47905     if (typeof(type) == 'undefined') {
47906         var ia = Roo.htmleditor.Block.initAll;
47907         ia(body,'table');
47908         ia(body,'td');
47909         ia(body,'figure');
47910         return;
47911     }
47912     Roo.each(Roo.get(body).query(type), function(e) {
47913         Roo.htmleditor.Block.factory(e);    
47914     },this);
47915 };
47916 // question goes here... do we need to clear out this cache sometimes?
47917 // or show we make it relivant to the htmleditor.
47918 Roo.htmleditor.Block.cache = {};
47919
47920 Roo.htmleditor.Block.prototype = {
47921     
47922     node : false,
47923     
47924      // used by context menu
47925     friendly_name : 'Based Block',
47926     
47927     // text for button to delete this element
47928     deleteTitle : false,
47929     
47930     context : false,
47931     /**
47932      * Update a node with values from this object
47933      * @param {DomElement} node
47934      */
47935     updateElement : function(node)
47936     {
47937         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
47938     },
47939      /**
47940      * convert to plain HTML for calling insertAtCursor..
47941      */
47942     toHTML : function()
47943     {
47944         return Roo.DomHelper.markup(this.toObject());
47945     },
47946     /**
47947      * used by readEleemnt to extract data from a node
47948      * may need improving as it's pretty basic
47949      
47950      * @param {DomElement} node
47951      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
47952      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
47953      * @param {String} style the style property - eg. text-align
47954      */
47955     getVal : function(node, tag, attr, style)
47956     {
47957         var n = node;
47958         if (tag !== true && n.tagName != tag.toUpperCase()) {
47959             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
47960             // but kiss for now.
47961             n = node.getElementsByTagName(tag).item(0);
47962         }
47963         if (!n) {
47964             return '';
47965         }
47966         if (attr === false) {
47967             return n;
47968         }
47969         if (attr == 'html') {
47970             return n.innerHTML;
47971         }
47972         if (attr == 'style') {
47973             return n.style[style]; 
47974         }
47975         
47976         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
47977             
47978     },
47979     /**
47980      * create a DomHelper friendly object - for use with 
47981      * Roo.DomHelper.markup / overwrite / etc..
47982      * (override this)
47983      */
47984     toObject : function()
47985     {
47986         return {};
47987     },
47988       /**
47989      * Read a node that has a 'data-block' property - and extract the values from it.
47990      * @param {DomElement} node - the node
47991      */
47992     readElement : function(node)
47993     {
47994         
47995     } 
47996     
47997     
47998 };
47999
48000  
48001
48002 /**
48003  * @class Roo.htmleditor.BlockFigure
48004  * Block that has an image and a figcaption
48005  * @cfg {String} image_src the url for the image
48006  * @cfg {String} align (left|right) alignment for the block default left
48007  * @cfg {String} caption the text to appear below  (and in the alt tag)
48008  * @cfg {String} caption_display (block|none) display or not the caption
48009  * @cfg {String|number} image_width the width of the image number or %?
48010  * @cfg {String|number} image_height the height of the image number or %?
48011  * 
48012  * @constructor
48013  * Create a new Filter.
48014  * @param {Object} config Configuration options
48015  */
48016
48017 Roo.htmleditor.BlockFigure = function(cfg)
48018 {
48019     if (cfg.node) {
48020         this.readElement(cfg.node);
48021         this.updateElement(cfg.node);
48022     }
48023     Roo.apply(this, cfg);
48024 }
48025 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
48026  
48027     
48028     // setable values.
48029     image_src: '',
48030     align: 'center',
48031     caption : '',
48032     caption_display : 'block',
48033     width : '100%',
48034     cls : '',
48035     href: '',
48036     video_url : '',
48037     
48038     // margin: '2%', not used
48039     
48040     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
48041
48042     
48043     // used by context menu
48044     friendly_name : 'Image with caption',
48045     deleteTitle : "Delete Image and Caption",
48046     
48047     contextMenu : function(toolbar)
48048     {
48049         
48050         var block = function() {
48051             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48052         };
48053         
48054         
48055         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48056         
48057         var syncValue = toolbar.editorcore.syncValue;
48058         
48059         var fields = {};
48060         
48061         return [
48062              {
48063                 xtype : 'TextItem',
48064                 text : "Source: ",
48065                 xns : rooui.Toolbar  //Boostrap?
48066             },
48067             {
48068                 xtype : 'Button',
48069                 text: 'Change Image URL',
48070                  
48071                 listeners : {
48072                     click: function (btn, state)
48073                     {
48074                         var b = block();
48075                         
48076                         Roo.MessageBox.show({
48077                             title : "Image Source URL",
48078                             msg : "Enter the url for the image",
48079                             buttons: Roo.MessageBox.OKCANCEL,
48080                             fn: function(btn, val){
48081                                 if (btn != 'ok') {
48082                                     return;
48083                                 }
48084                                 b.image_src = val;
48085                                 b.updateElement();
48086                                 syncValue();
48087                                 toolbar.editorcore.onEditorEvent();
48088                             },
48089                             minWidth:250,
48090                             prompt:true,
48091                             //multiline: multiline,
48092                             modal : true,
48093                             value : b.image_src
48094                         });
48095                     }
48096                 },
48097                 xns : rooui.Toolbar
48098             },
48099          
48100             {
48101                 xtype : 'Button',
48102                 text: 'Change Link URL',
48103                  
48104                 listeners : {
48105                     click: function (btn, state)
48106                     {
48107                         var b = block();
48108                         
48109                         Roo.MessageBox.show({
48110                             title : "Link URL",
48111                             msg : "Enter the url for the link - leave blank to have no link",
48112                             buttons: Roo.MessageBox.OKCANCEL,
48113                             fn: function(btn, val){
48114                                 if (btn != 'ok') {
48115                                     return;
48116                                 }
48117                                 b.href = val;
48118                                 b.updateElement();
48119                                 syncValue();
48120                                 toolbar.editorcore.onEditorEvent();
48121                             },
48122                             minWidth:250,
48123                             prompt:true,
48124                             //multiline: multiline,
48125                             modal : true,
48126                             value : b.href
48127                         });
48128                     }
48129                 },
48130                 xns : rooui.Toolbar
48131             },
48132             {
48133                 xtype : 'Button',
48134                 text: 'Show Video URL',
48135                  
48136                 listeners : {
48137                     click: function (btn, state)
48138                     {
48139                         Roo.MessageBox.alert("Video URL",
48140                             block().video_url == '' ? 'This image is not linked ot a video' :
48141                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
48142                     }
48143                 },
48144                 xns : rooui.Toolbar
48145             },
48146             
48147             
48148             {
48149                 xtype : 'TextItem',
48150                 text : "Width: ",
48151                 xns : rooui.Toolbar  //Boostrap?
48152             },
48153             {
48154                 xtype : 'ComboBox',
48155                 allowBlank : false,
48156                 displayField : 'val',
48157                 editable : true,
48158                 listWidth : 100,
48159                 triggerAction : 'all',
48160                 typeAhead : true,
48161                 valueField : 'val',
48162                 width : 70,
48163                 name : 'width',
48164                 listeners : {
48165                     select : function (combo, r, index)
48166                     {
48167                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48168                         var b = block();
48169                         b.width = r.get('val');
48170                         b.updateElement();
48171                         syncValue();
48172                         toolbar.editorcore.onEditorEvent();
48173                     }
48174                 },
48175                 xns : rooui.form,
48176                 store : {
48177                     xtype : 'SimpleStore',
48178                     data : [
48179                         ['50%'],
48180                         ['80%'],
48181                         ['100%']
48182                     ],
48183                     fields : [ 'val'],
48184                     xns : Roo.data
48185                 }
48186             },
48187             {
48188                 xtype : 'TextItem',
48189                 text : "Align: ",
48190                 xns : rooui.Toolbar  //Boostrap?
48191             },
48192             {
48193                 xtype : 'ComboBox',
48194                 allowBlank : false,
48195                 displayField : 'val',
48196                 editable : true,
48197                 listWidth : 100,
48198                 triggerAction : 'all',
48199                 typeAhead : true,
48200                 valueField : 'val',
48201                 width : 70,
48202                 name : 'align',
48203                 listeners : {
48204                     select : function (combo, r, index)
48205                     {
48206                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48207                         var b = block();
48208                         b.align = r.get('val');
48209                         b.updateElement();
48210                         syncValue();
48211                         toolbar.editorcore.onEditorEvent();
48212                     }
48213                 },
48214                 xns : rooui.form,
48215                 store : {
48216                     xtype : 'SimpleStore',
48217                     data : [
48218                         ['left'],
48219                         ['right'],
48220                         ['center']
48221                     ],
48222                     fields : [ 'val'],
48223                     xns : Roo.data
48224                 }
48225             },
48226             
48227             
48228             {
48229                 xtype : 'Button',
48230                 text: 'Hide Caption',
48231                 name : 'caption_display',
48232                 pressed : false,
48233                 enableToggle : true,
48234                 setValue : function(v) {
48235                     // this trigger toggle.
48236                      
48237                     this.setText(v ? "Hide Caption" : "Show Caption");
48238                     this.setPressed(v != 'block');
48239                 },
48240                 listeners : {
48241                     toggle: function (btn, state)
48242                     {
48243                         var b  = block();
48244                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48245                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48246                         b.updateElement();
48247                         syncValue();
48248                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48249                         toolbar.editorcore.onEditorEvent();
48250                     }
48251                 },
48252                 xns : rooui.Toolbar
48253             }
48254         ];
48255         
48256     },
48257     /**
48258      * create a DomHelper friendly object - for use with
48259      * Roo.DomHelper.markup / overwrite / etc..
48260      */
48261     toObject : function()
48262     {
48263         var d = document.createElement('div');
48264         d.innerHTML = this.caption;
48265         
48266         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
48267         
48268         var iw = this.align == 'center' ? this.width : '100%';
48269         var img =   {
48270             tag : 'img',
48271             contenteditable : 'false',
48272             src : this.image_src,
48273             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48274             style: {
48275                 width : iw,
48276                 maxWidth : iw + ' !important', // this is not getting rendered?
48277                 margin : m  
48278                 
48279             }
48280         };
48281         /*
48282         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48283                     '<a href="{2}">' + 
48284                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
48285                     '</a>' + 
48286                 '</div>',
48287         */
48288                 
48289         if (this.href.length > 0) {
48290             img = {
48291                 tag : 'a',
48292                 href: this.href,
48293                 contenteditable : 'true',
48294                 cn : [
48295                     img
48296                 ]
48297             };
48298         }
48299         
48300         
48301         if (this.video_url.length > 0) {
48302             img = {
48303                 tag : 'div',
48304                 cls : this.cls,
48305                 frameborder : 0,
48306                 allowfullscreen : true,
48307                 width : 420,  // these are for video tricks - that we replace the outer
48308                 height : 315,
48309                 src : this.video_url,
48310                 cn : [
48311                     img
48312                 ]
48313             };
48314         }
48315         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48316         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48317         
48318   
48319         var ret =   {
48320             tag: 'figure',
48321             'data-block' : 'Figure',
48322             'data-width' : this.width, 
48323             contenteditable : 'false',
48324             
48325             style : {
48326                 display: 'block',
48327                 float :  this.align ,
48328                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48329                 width : this.align == 'center' ? '100%' : this.width,
48330                 margin:  '0px',
48331                 padding: this.align == 'center' ? '0' : '0 10px' ,
48332                 textAlign : this.align   // seems to work for email..
48333                 
48334             },
48335            
48336             
48337             align : this.align,
48338             cn : [
48339                 img,
48340               
48341                 {
48342                     tag: 'figcaption',
48343                     'data-display' : this.caption_display,
48344                     style : {
48345                         textAlign : 'left',
48346                         fontSize : '16px',
48347                         lineHeight : '24px',
48348                         display : this.caption_display,
48349                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
48350                         margin: m,
48351                         width: this.align == 'center' ?  this.width : '100%' 
48352                     
48353                          
48354                     },
48355                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
48356                     cn : [
48357                         {
48358                             tag: 'div',
48359                             style  : {
48360                                 marginTop : '16px',
48361                                 textAlign : 'left'
48362                             },
48363                             align: 'left',
48364                             cn : [
48365                                 {
48366                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
48367                                     tag : 'i',
48368                                     contenteditable : true,
48369                                     html : captionhtml
48370                                 }
48371                                 
48372                             ]
48373                         }
48374                         
48375                     ]
48376                     
48377                 }
48378             ]
48379         };
48380         return ret;
48381          
48382     },
48383     
48384     readElement : function(node)
48385     {
48386         // this should not really come from the link...
48387         this.video_url = this.getVal(node, 'div', 'src');
48388         this.cls = this.getVal(node, 'div', 'class');
48389         this.href = this.getVal(node, 'a', 'href');
48390         
48391         
48392         this.image_src = this.getVal(node, 'img', 'src');
48393          
48394         this.align = this.getVal(node, 'figure', 'align');
48395         var figcaption = this.getVal(node, 'figcaption', false);
48396         if (figcaption !== '') {
48397             this.caption = this.getVal(figcaption, 'i', 'html');
48398         }
48399         
48400
48401         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48402         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48403         this.width = this.getVal(node, true, 'data-width');
48404         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48405         
48406     },
48407     removeNode : function()
48408     {
48409         return this.node;
48410     }
48411     
48412   
48413    
48414      
48415     
48416     
48417     
48418     
48419 })
48420
48421  
48422
48423 /**
48424  * @class Roo.htmleditor.BlockTable
48425  * Block that manages a table
48426  * 
48427  * @constructor
48428  * Create a new Filter.
48429  * @param {Object} config Configuration options
48430  */
48431
48432 Roo.htmleditor.BlockTable = function(cfg)
48433 {
48434     if (cfg.node) {
48435         this.readElement(cfg.node);
48436         this.updateElement(cfg.node);
48437     }
48438     Roo.apply(this, cfg);
48439     if (!cfg.node) {
48440         this.rows = [];
48441         for(var r = 0; r < this.no_row; r++) {
48442             this.rows[r] = [];
48443             for(var c = 0; c < this.no_col; c++) {
48444                 this.rows[r][c] = this.emptyCell();
48445             }
48446         }
48447     }
48448     
48449     
48450 }
48451 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48452  
48453     rows : false,
48454     no_col : 1,
48455     no_row : 1,
48456     
48457     
48458     width: '100%',
48459     
48460     // used by context menu
48461     friendly_name : 'Table',
48462     deleteTitle : 'Delete Table',
48463     // context menu is drawn once..
48464     
48465     contextMenu : function(toolbar)
48466     {
48467         
48468         var block = function() {
48469             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48470         };
48471         
48472         
48473         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48474         
48475         var syncValue = toolbar.editorcore.syncValue;
48476         
48477         var fields = {};
48478         
48479         return [
48480             {
48481                 xtype : 'TextItem',
48482                 text : "Width: ",
48483                 xns : rooui.Toolbar  //Boostrap?
48484             },
48485             {
48486                 xtype : 'ComboBox',
48487                 allowBlank : false,
48488                 displayField : 'val',
48489                 editable : true,
48490                 listWidth : 100,
48491                 triggerAction : 'all',
48492                 typeAhead : true,
48493                 valueField : 'val',
48494                 width : 100,
48495                 name : 'width',
48496                 listeners : {
48497                     select : function (combo, r, index)
48498                     {
48499                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48500                         var b = block();
48501                         b.width = r.get('val');
48502                         b.updateElement();
48503                         syncValue();
48504                         toolbar.editorcore.onEditorEvent();
48505                     }
48506                 },
48507                 xns : rooui.form,
48508                 store : {
48509                     xtype : 'SimpleStore',
48510                     data : [
48511                         ['100%'],
48512                         ['auto']
48513                     ],
48514                     fields : [ 'val'],
48515                     xns : Roo.data
48516                 }
48517             },
48518             // -------- Cols
48519             
48520             {
48521                 xtype : 'TextItem',
48522                 text : "Columns: ",
48523                 xns : rooui.Toolbar  //Boostrap?
48524             },
48525          
48526             {
48527                 xtype : 'Button',
48528                 text: '-',
48529                 listeners : {
48530                     click : function (_self, e)
48531                     {
48532                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48533                         block().removeColumn();
48534                         syncValue();
48535                         toolbar.editorcore.onEditorEvent();
48536                     }
48537                 },
48538                 xns : rooui.Toolbar
48539             },
48540             {
48541                 xtype : 'Button',
48542                 text: '+',
48543                 listeners : {
48544                     click : function (_self, e)
48545                     {
48546                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48547                         block().addColumn();
48548                         syncValue();
48549                         toolbar.editorcore.onEditorEvent();
48550                     }
48551                 },
48552                 xns : rooui.Toolbar
48553             },
48554             // -------- ROWS
48555             {
48556                 xtype : 'TextItem',
48557                 text : "Rows: ",
48558                 xns : rooui.Toolbar  //Boostrap?
48559             },
48560          
48561             {
48562                 xtype : 'Button',
48563                 text: '-',
48564                 listeners : {
48565                     click : function (_self, e)
48566                     {
48567                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48568                         block().removeRow();
48569                         syncValue();
48570                         toolbar.editorcore.onEditorEvent();
48571                     }
48572                 },
48573                 xns : rooui.Toolbar
48574             },
48575             {
48576                 xtype : 'Button',
48577                 text: '+',
48578                 listeners : {
48579                     click : function (_self, e)
48580                     {
48581                         block().addRow();
48582                         syncValue();
48583                         toolbar.editorcore.onEditorEvent();
48584                     }
48585                 },
48586                 xns : rooui.Toolbar
48587             },
48588             // -------- ROWS
48589             {
48590                 xtype : 'Button',
48591                 text: 'Reset Column Widths',
48592                 listeners : {
48593                     
48594                     click : function (_self, e)
48595                     {
48596                         block().resetWidths();
48597                         syncValue();
48598                         toolbar.editorcore.onEditorEvent();
48599                     }
48600                 },
48601                 xns : rooui.Toolbar
48602             } 
48603             
48604             
48605             
48606         ];
48607         
48608     },
48609     
48610     
48611   /**
48612      * create a DomHelper friendly object - for use with
48613      * Roo.DomHelper.markup / overwrite / etc..
48614      * ?? should it be called with option to hide all editing features?
48615      */
48616     toObject : function()
48617     {
48618         
48619         var ret = {
48620             tag : 'table',
48621             contenteditable : 'false', // this stops cell selection from picking the table.
48622             'data-block' : 'Table',
48623             style : {
48624                 width:  this.width,
48625                 border : 'solid 1px #000', // ??? hard coded?
48626                 'border-collapse' : 'collapse' 
48627             },
48628             cn : [
48629                 { tag : 'tbody' , cn : [] }
48630             ]
48631         };
48632         
48633         // do we have a head = not really 
48634         var ncols = 0;
48635         Roo.each(this.rows, function( row ) {
48636             var tr = {
48637                 tag: 'tr',
48638                 style : {
48639                     margin: '6px',
48640                     border : 'solid 1px #000',
48641                     textAlign : 'left' 
48642                 },
48643                 cn : [ ]
48644             };
48645             
48646             ret.cn[0].cn.push(tr);
48647             // does the row have any properties? ?? height?
48648             var nc = 0;
48649             Roo.each(row, function( cell ) {
48650                 
48651                 var td = {
48652                     tag : 'td',
48653                     contenteditable :  'true',
48654                     'data-block' : 'Td',
48655                     html : cell.html,
48656                     style : cell.style
48657                 };
48658                 if (cell.colspan > 1) {
48659                     td.colspan = cell.colspan ;
48660                     nc += cell.colspan;
48661                 } else {
48662                     nc++;
48663                 }
48664                 if (cell.rowspan > 1) {
48665                     td.rowspan = cell.rowspan ;
48666                 }
48667                 
48668                 
48669                 // widths ?
48670                 tr.cn.push(td);
48671                     
48672                 
48673             }, this);
48674             ncols = Math.max(nc, ncols);
48675             
48676             
48677         }, this);
48678         // add the header row..
48679         
48680         ncols++;
48681          
48682         
48683         return ret;
48684          
48685     },
48686     
48687     readElement : function(node)
48688     {
48689         node  = node ? node : this.node ;
48690         this.width = this.getVal(node, true, 'style', 'width') || '100%';
48691         
48692         this.rows = [];
48693         this.no_row = 0;
48694         var trs = Array.from(node.rows);
48695         trs.forEach(function(tr) {
48696             var row =  [];
48697             this.rows.push(row);
48698             
48699             this.no_row++;
48700             var no_column = 0;
48701             Array.from(tr.cells).forEach(function(td) {
48702                 
48703                 var add = {
48704                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48705                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48706                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48707                     html : td.innerHTML
48708                 };
48709                 no_column += add.colspan;
48710                      
48711                 
48712                 row.push(add);
48713                 
48714                 
48715             },this);
48716             this.no_col = Math.max(this.no_col, no_column);
48717             
48718             
48719         },this);
48720         
48721         
48722     },
48723     normalizeRows: function()
48724     {
48725         var ret= [];
48726         var rid = -1;
48727         this.rows.forEach(function(row) {
48728             rid++;
48729             ret[rid] = [];
48730             row = this.normalizeRow(row);
48731             var cid = 0;
48732             row.forEach(function(c) {
48733                 while (typeof(ret[rid][cid]) != 'undefined') {
48734                     cid++;
48735                 }
48736                 if (typeof(ret[rid]) == 'undefined') {
48737                     ret[rid] = [];
48738                 }
48739                 ret[rid][cid] = c;
48740                 c.row = rid;
48741                 c.col = cid;
48742                 if (c.rowspan < 2) {
48743                     return;
48744                 }
48745                 
48746                 for(var i = 1 ;i < c.rowspan; i++) {
48747                     if (typeof(ret[rid+i]) == 'undefined') {
48748                         ret[rid+i] = [];
48749                     }
48750                     ret[rid+i][cid] = c;
48751                 }
48752             });
48753         }, this);
48754         return ret;
48755     
48756     },
48757     
48758     normalizeRow: function(row)
48759     {
48760         var ret= [];
48761         row.forEach(function(c) {
48762             if (c.colspan < 2) {
48763                 ret.push(c);
48764                 return;
48765             }
48766             for(var i =0 ;i < c.colspan; i++) {
48767                 ret.push(c);
48768             }
48769         });
48770         return ret;
48771     
48772     },
48773     
48774     deleteColumn : function(sel)
48775     {
48776         if (!sel || sel.type != 'col') {
48777             return;
48778         }
48779         if (this.no_col < 2) {
48780             return;
48781         }
48782         
48783         this.rows.forEach(function(row) {
48784             var cols = this.normalizeRow(row);
48785             var col = cols[sel.col];
48786             if (col.colspan > 1) {
48787                 col.colspan --;
48788             } else {
48789                 row.remove(col);
48790             }
48791             
48792         }, this);
48793         this.no_col--;
48794         
48795     },
48796     removeColumn : function()
48797     {
48798         this.deleteColumn({
48799             type: 'col',
48800             col : this.no_col-1
48801         });
48802         this.updateElement();
48803     },
48804     
48805      
48806     addColumn : function()
48807     {
48808         
48809         this.rows.forEach(function(row) {
48810             row.push(this.emptyCell());
48811            
48812         }, this);
48813         this.updateElement();
48814     },
48815     
48816     deleteRow : function(sel)
48817     {
48818         if (!sel || sel.type != 'row') {
48819             return;
48820         }
48821         
48822         if (this.no_row < 2) {
48823             return;
48824         }
48825         
48826         var rows = this.normalizeRows();
48827         
48828         
48829         rows[sel.row].forEach(function(col) {
48830             if (col.rowspan > 1) {
48831                 col.rowspan--;
48832             } else {
48833                 col.remove = 1; // flage it as removed.
48834             }
48835             
48836         }, this);
48837         var newrows = [];
48838         this.rows.forEach(function(row) {
48839             newrow = [];
48840             row.forEach(function(c) {
48841                 if (typeof(c.remove) == 'undefined') {
48842                     newrow.push(c);
48843                 }
48844                 
48845             });
48846             if (newrow.length > 0) {
48847                 newrows.push(row);
48848             }
48849         });
48850         this.rows =  newrows;
48851         
48852         
48853         
48854         this.no_row--;
48855         this.updateElement();
48856         
48857     },
48858     removeRow : function()
48859     {
48860         this.deleteRow({
48861             type: 'row',
48862             row : this.no_row-1
48863         });
48864         
48865     },
48866     
48867      
48868     addRow : function()
48869     {
48870         
48871         var row = [];
48872         for (var i = 0; i < this.no_col; i++ ) {
48873             
48874             row.push(this.emptyCell());
48875            
48876         }
48877         this.rows.push(row);
48878         this.updateElement();
48879         
48880     },
48881      
48882     // the default cell object... at present...
48883     emptyCell : function() {
48884         return (new Roo.htmleditor.BlockTd({})).toObject();
48885         
48886      
48887     },
48888     
48889     removeNode : function()
48890     {
48891         return this.node;
48892     },
48893     
48894     
48895     
48896     resetWidths : function()
48897     {
48898         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
48899             var nn = Roo.htmleditor.Block.factory(n);
48900             nn.width = '';
48901             nn.updateElement(n);
48902         });
48903     }
48904     
48905     
48906     
48907     
48908 })
48909
48910 /**
48911  *
48912  * editing a TD?
48913  *
48914  * since selections really work on the table cell, then editing really should work from there
48915  *
48916  * The original plan was to support merging etc... - but that may not be needed yet..
48917  *
48918  * So this simple version will support:
48919  *   add/remove cols
48920  *   adjust the width +/-
48921  *   reset the width...
48922  *   
48923  *
48924  */
48925
48926
48927  
48928
48929 /**
48930  * @class Roo.htmleditor.BlockTable
48931  * Block that manages a table
48932  * 
48933  * @constructor
48934  * Create a new Filter.
48935  * @param {Object} config Configuration options
48936  */
48937
48938 Roo.htmleditor.BlockTd = function(cfg)
48939 {
48940     if (cfg.node) {
48941         this.readElement(cfg.node);
48942         this.updateElement(cfg.node);
48943     }
48944     Roo.apply(this, cfg);
48945      
48946     
48947     
48948 }
48949 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
48950  
48951     node : false,
48952     
48953     width: '',
48954     textAlign : 'left',
48955     valign : 'top',
48956     
48957     colspan : 1,
48958     rowspan : 1,
48959     
48960     
48961     // used by context menu
48962     friendly_name : 'Table Cell',
48963     deleteTitle : false, // use our customer delete
48964     
48965     // context menu is drawn once..
48966     
48967     contextMenu : function(toolbar)
48968     {
48969         
48970         var cell = function() {
48971             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48972         };
48973         
48974         var table = function() {
48975             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
48976         };
48977         
48978         var lr = false;
48979         var saveSel = function()
48980         {
48981             lr = toolbar.editorcore.getSelection().getRangeAt(0);
48982         }
48983         var restoreSel = function()
48984         {
48985             if (lr) {
48986                 (function() {
48987                     toolbar.editorcore.focus();
48988                     var cr = toolbar.editorcore.getSelection();
48989                     cr.removeAllRanges();
48990                     cr.addRange(lr);
48991                     toolbar.editorcore.onEditorEvent();
48992                 }).defer(10, this);
48993                 
48994                 
48995             }
48996         }
48997         
48998         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48999         
49000         var syncValue = toolbar.editorcore.syncValue;
49001         
49002         var fields = {};
49003         
49004         return [
49005             {
49006                 xtype : 'Button',
49007                 text : 'Edit Table',
49008                 listeners : {
49009                     click : function() {
49010                         var t = toolbar.tb.selectedNode.closest('table');
49011                         toolbar.editorcore.selectNode(t);
49012                         toolbar.editorcore.onEditorEvent();                        
49013                     }
49014                 }
49015                 
49016             },
49017               
49018            
49019              
49020             {
49021                 xtype : 'TextItem',
49022                 text : "Column Width: ",
49023                  xns : rooui.Toolbar 
49024                
49025             },
49026             {
49027                 xtype : 'Button',
49028                 text: '-',
49029                 listeners : {
49030                     click : function (_self, e)
49031                     {
49032                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49033                         cell().shrinkColumn();
49034                         syncValue();
49035                          toolbar.editorcore.onEditorEvent();
49036                     }
49037                 },
49038                 xns : rooui.Toolbar
49039             },
49040             {
49041                 xtype : 'Button',
49042                 text: '+',
49043                 listeners : {
49044                     click : function (_self, e)
49045                     {
49046                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49047                         cell().growColumn();
49048                         syncValue();
49049                         toolbar.editorcore.onEditorEvent();
49050                     }
49051                 },
49052                 xns : rooui.Toolbar
49053             },
49054             
49055             {
49056                 xtype : 'TextItem',
49057                 text : "Vertical Align: ",
49058                 xns : rooui.Toolbar  //Boostrap?
49059             },
49060             {
49061                 xtype : 'ComboBox',
49062                 allowBlank : false,
49063                 displayField : 'val',
49064                 editable : true,
49065                 listWidth : 100,
49066                 triggerAction : 'all',
49067                 typeAhead : true,
49068                 valueField : 'val',
49069                 width : 100,
49070                 name : 'valign',
49071                 listeners : {
49072                     select : function (combo, r, index)
49073                     {
49074                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49075                         var b = cell();
49076                         b.valign = r.get('val');
49077                         b.updateElement();
49078                         syncValue();
49079                         toolbar.editorcore.onEditorEvent();
49080                     }
49081                 },
49082                 xns : rooui.form,
49083                 store : {
49084                     xtype : 'SimpleStore',
49085                     data : [
49086                         ['top'],
49087                         ['middle'],
49088                         ['bottom'] // there are afew more... 
49089                     ],
49090                     fields : [ 'val'],
49091                     xns : Roo.data
49092                 }
49093             },
49094             
49095             {
49096                 xtype : 'TextItem',
49097                 text : "Merge Cells: ",
49098                  xns : rooui.Toolbar 
49099                
49100             },
49101             
49102             
49103             {
49104                 xtype : 'Button',
49105                 text: 'Right',
49106                 listeners : {
49107                     click : function (_self, e)
49108                     {
49109                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49110                         cell().mergeRight();
49111                         //block().growColumn();
49112                         syncValue();
49113                         toolbar.editorcore.onEditorEvent();
49114                     }
49115                 },
49116                 xns : rooui.Toolbar
49117             },
49118              
49119             {
49120                 xtype : 'Button',
49121                 text: 'Below',
49122                 listeners : {
49123                     click : function (_self, e)
49124                     {
49125                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49126                         cell().mergeBelow();
49127                         //block().growColumn();
49128                         syncValue();
49129                         toolbar.editorcore.onEditorEvent();
49130                     }
49131                 },
49132                 xns : rooui.Toolbar
49133             },
49134             {
49135                 xtype : 'TextItem',
49136                 text : "| ",
49137                  xns : rooui.Toolbar 
49138                
49139             },
49140             
49141             {
49142                 xtype : 'Button',
49143                 text: 'Split',
49144                 listeners : {
49145                     click : function (_self, e)
49146                     {
49147                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49148                         cell().split();
49149                         syncValue();
49150                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49151                         toolbar.editorcore.onEditorEvent();
49152                                              
49153                     }
49154                 },
49155                 xns : rooui.Toolbar
49156             },
49157             {
49158                 xtype : 'Fill',
49159                 xns : rooui.Toolbar 
49160                
49161             },
49162         
49163           
49164             {
49165                 xtype : 'Button',
49166                 text: 'Delete',
49167                  
49168                 xns : rooui.Toolbar,
49169                 menu : {
49170                     xtype : 'Menu',
49171                     xns : rooui.menu,
49172                     items : [
49173                         {
49174                             xtype : 'Item',
49175                             html: 'Column',
49176                             listeners : {
49177                                 click : function (_self, e)
49178                                 {
49179                                     var t = table();
49180                                     
49181                                     cell().deleteColumn();
49182                                     syncValue();
49183                                     toolbar.editorcore.selectNode(t.node);
49184                                     toolbar.editorcore.onEditorEvent();   
49185                                 }
49186                             },
49187                             xns : rooui.menu
49188                         },
49189                         {
49190                             xtype : 'Item',
49191                             html: 'Row',
49192                             listeners : {
49193                                 click : function (_self, e)
49194                                 {
49195                                     var t = table();
49196                                     cell().deleteRow();
49197                                     syncValue();
49198                                     
49199                                     toolbar.editorcore.selectNode(t.node);
49200                                     toolbar.editorcore.onEditorEvent();   
49201                                                          
49202                                 }
49203                             },
49204                             xns : rooui.menu
49205                         },
49206                        {
49207                             xtype : 'Separator',
49208                             xns : rooui.menu
49209                         },
49210                         {
49211                             xtype : 'Item',
49212                             html: 'Table',
49213                             listeners : {
49214                                 click : function (_self, e)
49215                                 {
49216                                     var t = table();
49217                                     var nn = t.node.nextSibling || t.node.previousSibling;
49218                                     t.node.parentNode.removeChild(t.node);
49219                                     if (nn) { 
49220                                         toolbar.editorcore.selectNode(nn, true);
49221                                     }
49222                                     toolbar.editorcore.onEditorEvent();   
49223                                                          
49224                                 }
49225                             },
49226                             xns : rooui.menu
49227                         }
49228                     ]
49229                 }
49230             }
49231             
49232             // align... << fixme
49233             
49234         ];
49235         
49236     },
49237     
49238     
49239   /**
49240      * create a DomHelper friendly object - for use with
49241      * Roo.DomHelper.markup / overwrite / etc..
49242      * ?? should it be called with option to hide all editing features?
49243      */
49244  /**
49245      * create a DomHelper friendly object - for use with
49246      * Roo.DomHelper.markup / overwrite / etc..
49247      * ?? should it be called with option to hide all editing features?
49248      */
49249     toObject : function()
49250     {
49251         
49252         var ret = {
49253             tag : 'td',
49254             contenteditable : 'true', // this stops cell selection from picking the table.
49255             'data-block' : 'Td',
49256             valign : this.valign,
49257             style : {  
49258                 'text-align' :  this.textAlign,
49259                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49260                 'border-collapse' : 'collapse',
49261                 padding : '6px', // 8 for desktop / 4 for mobile
49262                 'vertical-align': this.valign
49263             },
49264             html : this.html
49265         };
49266         if (this.width != '') {
49267             ret.width = this.width;
49268             ret.style.width = this.width;
49269         }
49270         
49271         
49272         if (this.colspan > 1) {
49273             ret.colspan = this.colspan ;
49274         } 
49275         if (this.rowspan > 1) {
49276             ret.rowspan = this.rowspan ;
49277         }
49278         
49279            
49280         
49281         return ret;
49282          
49283     },
49284     
49285     readElement : function(node)
49286     {
49287         node  = node ? node : this.node ;
49288         this.width = node.style.width;
49289         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49290         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49291         this.html = node.innerHTML;
49292         
49293         
49294     },
49295      
49296     // the default cell object... at present...
49297     emptyCell : function() {
49298         return {
49299             colspan :  1,
49300             rowspan :  1,
49301             textAlign : 'left',
49302             html : "&nbsp;" // is this going to be editable now?
49303         };
49304      
49305     },
49306     
49307     removeNode : function()
49308     {
49309         return this.node.closest('table');
49310          
49311     },
49312     
49313     cellData : false,
49314     
49315     colWidths : false,
49316     
49317     toTableArray  : function()
49318     {
49319         var ret = [];
49320         var tab = this.node.closest('tr').closest('table');
49321         Array.from(tab.rows).forEach(function(r, ri){
49322             ret[ri] = [];
49323         });
49324         var rn = 0;
49325         this.colWidths = [];
49326         var all_auto = true;
49327         Array.from(tab.rows).forEach(function(r, ri){
49328             
49329             var cn = 0;
49330             Array.from(r.cells).forEach(function(ce, ci){
49331                 var c =  {
49332                     cell : ce,
49333                     row : rn,
49334                     col: cn,
49335                     colspan : ce.colSpan,
49336                     rowspan : ce.rowSpan
49337                 };
49338                 if (ce.isEqualNode(this.node)) {
49339                     this.cellData = c;
49340                 }
49341                 // if we have been filled up by a row?
49342                 if (typeof(ret[rn][cn]) != 'undefined') {
49343                     while(typeof(ret[rn][cn]) != 'undefined') {
49344                         cn++;
49345                     }
49346                     c.col = cn;
49347                 }
49348                 
49349                 if (typeof(this.colWidths[cn]) == 'undefined') {
49350                     this.colWidths[cn] =   ce.style.width;
49351                     if (this.colWidths[cn] != '') {
49352                         all_auto = false;
49353                     }
49354                 }
49355                 
49356                 
49357                 if (c.colspan < 2 && c.rowspan < 2 ) {
49358                     ret[rn][cn] = c;
49359                     cn++;
49360                     return;
49361                 }
49362                 for(var j = 0; j < c.rowspan; j++) {
49363                     if (typeof(ret[rn+j]) == 'undefined') {
49364                         continue; // we have a problem..
49365                     }
49366                     ret[rn+j][cn] = c;
49367                     for(var i = 0; i < c.colspan; i++) {
49368                         ret[rn+j][cn+i] = c;
49369                     }
49370                 }
49371                 
49372                 cn += c.colspan;
49373             }, this);
49374             rn++;
49375         }, this);
49376         
49377         // initalize widths.?
49378         // either all widths or no widths..
49379         if (all_auto) {
49380             this.colWidths[0] = false; // no widths flag.
49381         }
49382         
49383         
49384         return ret;
49385         
49386     },
49387     
49388     
49389     
49390     
49391     mergeRight: function()
49392     {
49393          
49394         // get the contents of the next cell along..
49395         var tr = this.node.closest('tr');
49396         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49397         if (i >= tr.childNodes.length - 1) {
49398             return; // no cells on right to merge with.
49399         }
49400         var table = this.toTableArray();
49401         
49402         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49403             return; // nothing right?
49404         }
49405         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49406         // right cell - must be same rowspan and on the same row.
49407         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49408             return; // right hand side is not same rowspan.
49409         }
49410         
49411         
49412         
49413         this.node.innerHTML += ' ' + rc.cell.innerHTML;
49414         tr.removeChild(rc.cell);
49415         this.colspan += rc.colspan;
49416         this.node.setAttribute('colspan', this.colspan);
49417
49418     },
49419     
49420     
49421     mergeBelow : function()
49422     {
49423         var table = this.toTableArray();
49424         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49425             return; // no row below
49426         }
49427         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49428             return; // nothing right?
49429         }
49430         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49431         
49432         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49433             return; // right hand side is not same rowspan.
49434         }
49435         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
49436         rc.cell.parentNode.removeChild(rc.cell);
49437         this.rowspan += rc.rowspan;
49438         this.node.setAttribute('rowspan', this.rowspan);
49439     },
49440     
49441     split: function()
49442     {
49443         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49444             return;
49445         }
49446         var table = this.toTableArray();
49447         var cd = this.cellData;
49448         this.rowspan = 1;
49449         this.colspan = 1;
49450         
49451         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49452             
49453             
49454             
49455             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49456                 if (r == cd.row && c == cd.col) {
49457                     this.node.removeAttribute('rowspan');
49458                     this.node.removeAttribute('colspan');
49459                     continue;
49460                 }
49461                  
49462                 var ntd = this.node.cloneNode(); // which col/row should be 0..
49463                 ntd.removeAttribute('id'); //
49464                 //ntd.style.width  = '';
49465                 ntd.innerHTML = '';
49466                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
49467             }
49468             
49469         }
49470         this.redrawAllCells(table);
49471         
49472          
49473         
49474     },
49475     
49476     
49477     
49478     redrawAllCells: function(table)
49479     {
49480         
49481          
49482         var tab = this.node.closest('tr').closest('table');
49483         var ctr = tab.rows[0].parentNode;
49484         Array.from(tab.rows).forEach(function(r, ri){
49485             
49486             Array.from(r.cells).forEach(function(ce, ci){
49487                 ce.parentNode.removeChild(ce);
49488             });
49489             r.parentNode.removeChild(r);
49490         });
49491         for(var r = 0 ; r < table.length; r++) {
49492             var re = tab.rows[r];
49493             
49494             var re = tab.ownerDocument.createElement('tr');
49495             ctr.appendChild(re);
49496             for(var c = 0 ; c < table[r].length; c++) {
49497                 if (table[r][c].cell === false) {
49498                     continue;
49499                 }
49500                 
49501                 re.appendChild(table[r][c].cell);
49502                  
49503                 table[r][c].cell = false;
49504             }
49505         }
49506         
49507     },
49508     updateWidths : function(table)
49509     {
49510         for(var r = 0 ; r < table.length; r++) {
49511            
49512             for(var c = 0 ; c < table[r].length; c++) {
49513                 if (table[r][c].cell === false) {
49514                     continue;
49515                 }
49516                 
49517                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49518                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49519                     el.width = Math.floor(this.colWidths[c])  +'%';
49520                     el.updateElement(el.node);
49521                 }
49522                 table[r][c].cell = false; // done
49523             }
49524         }
49525     },
49526     normalizeWidths : function(table)
49527     {
49528     
49529         if (this.colWidths[0] === false) {
49530             var nw = 100.0 / this.colWidths.length;
49531             this.colWidths.forEach(function(w,i) {
49532                 this.colWidths[i] = nw;
49533             },this);
49534             return;
49535         }
49536     
49537         var t = 0, missing = [];
49538         
49539         this.colWidths.forEach(function(w,i) {
49540             //if you mix % and
49541             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49542             var add =  this.colWidths[i];
49543             if (add > 0) {
49544                 t+=add;
49545                 return;
49546             }
49547             missing.push(i);
49548             
49549             
49550         },this);
49551         var nc = this.colWidths.length;
49552         if (missing.length) {
49553             var mult = (nc - missing.length) / (1.0 * nc);
49554             var t = mult * t;
49555             var ew = (100 -t) / (1.0 * missing.length);
49556             this.colWidths.forEach(function(w,i) {
49557                 if (w > 0) {
49558                     this.colWidths[i] = w * mult;
49559                     return;
49560                 }
49561                 
49562                 this.colWidths[i] = ew;
49563             }, this);
49564             // have to make up numbers..
49565              
49566         }
49567         // now we should have all the widths..
49568         
49569     
49570     },
49571     
49572     shrinkColumn : function()
49573     {
49574         var table = this.toTableArray();
49575         this.normalizeWidths(table);
49576         var col = this.cellData.col;
49577         var nw = this.colWidths[col] * 0.8;
49578         if (nw < 5) {
49579             return;
49580         }
49581         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49582         this.colWidths.forEach(function(w,i) {
49583             if (i == col) {
49584                  this.colWidths[i] = nw;
49585                 return;
49586             }
49587             this.colWidths[i] += otherAdd
49588         }, this);
49589         this.updateWidths(table);
49590          
49591     },
49592     growColumn : function()
49593     {
49594         var table = this.toTableArray();
49595         this.normalizeWidths(table);
49596         var col = this.cellData.col;
49597         var nw = this.colWidths[col] * 1.2;
49598         if (nw > 90) {
49599             return;
49600         }
49601         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49602         this.colWidths.forEach(function(w,i) {
49603             if (i == col) {
49604                 this.colWidths[i] = nw;
49605                 return;
49606             }
49607             this.colWidths[i] -= otherSub
49608         }, this);
49609         this.updateWidths(table);
49610          
49611     },
49612     deleteRow : function()
49613     {
49614         // delete this rows 'tr'
49615         // if any of the cells in this row have a rowspan > 1 && row!= this row..
49616         // then reduce the rowspan.
49617         var table = this.toTableArray();
49618         // this.cellData.row;
49619         for (var i =0;i< table[this.cellData.row].length ; i++) {
49620             var c = table[this.cellData.row][i];
49621             if (c.row != this.cellData.row) {
49622                 
49623                 c.rowspan--;
49624                 c.cell.setAttribute('rowspan', c.rowspan);
49625                 continue;
49626             }
49627             if (c.rowspan > 1) {
49628                 c.rowspan--;
49629                 c.cell.setAttribute('rowspan', c.rowspan);
49630             }
49631         }
49632         table.splice(this.cellData.row,1);
49633         this.redrawAllCells(table);
49634         
49635     },
49636     deleteColumn : function()
49637     {
49638         var table = this.toTableArray();
49639         
49640         for (var i =0;i< table.length ; i++) {
49641             var c = table[i][this.cellData.col];
49642             if (c.col != this.cellData.col) {
49643                 table[i][this.cellData.col].colspan--;
49644             } else if (c.colspan > 1) {
49645                 c.colspan--;
49646                 c.cell.setAttribute('colspan', c.colspan);
49647             }
49648             table[i].splice(this.cellData.col,1);
49649         }
49650         
49651         this.redrawAllCells(table);
49652     }
49653     
49654     
49655     
49656     
49657 })
49658
49659 //<script type="text/javascript">
49660
49661 /*
49662  * Based  Ext JS Library 1.1.1
49663  * Copyright(c) 2006-2007, Ext JS, LLC.
49664  * LGPL
49665  *
49666  */
49667  
49668 /**
49669  * @class Roo.HtmlEditorCore
49670  * @extends Roo.Component
49671  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49672  *
49673  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49674  */
49675
49676 Roo.HtmlEditorCore = function(config){
49677     
49678     
49679     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49680     
49681     
49682     this.addEvents({
49683         /**
49684          * @event initialize
49685          * Fires when the editor is fully initialized (including the iframe)
49686          * @param {Roo.HtmlEditorCore} this
49687          */
49688         initialize: true,
49689         /**
49690          * @event activate
49691          * Fires when the editor is first receives the focus. Any insertion must wait
49692          * until after this event.
49693          * @param {Roo.HtmlEditorCore} this
49694          */
49695         activate: true,
49696          /**
49697          * @event beforesync
49698          * Fires before the textarea is updated with content from the editor iframe. Return false
49699          * to cancel the sync.
49700          * @param {Roo.HtmlEditorCore} this
49701          * @param {String} html
49702          */
49703         beforesync: true,
49704          /**
49705          * @event beforepush
49706          * Fires before the iframe editor is updated with content from the textarea. Return false
49707          * to cancel the push.
49708          * @param {Roo.HtmlEditorCore} this
49709          * @param {String} html
49710          */
49711         beforepush: true,
49712          /**
49713          * @event sync
49714          * Fires when the textarea is updated with content from the editor iframe.
49715          * @param {Roo.HtmlEditorCore} this
49716          * @param {String} html
49717          */
49718         sync: true,
49719          /**
49720          * @event push
49721          * Fires when the iframe editor is updated with content from the textarea.
49722          * @param {Roo.HtmlEditorCore} this
49723          * @param {String} html
49724          */
49725         push: true,
49726         
49727         /**
49728          * @event editorevent
49729          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49730          * @param {Roo.HtmlEditorCore} this
49731          */
49732         editorevent: true 
49733          
49734         
49735     });
49736     
49737     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49738     
49739     // defaults : white / black...
49740     this.applyBlacklists();
49741     
49742     
49743     
49744 };
49745
49746
49747 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
49748
49749
49750      /**
49751      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
49752      */
49753     
49754     owner : false,
49755     
49756      /**
49757      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
49758      *                        Roo.resizable.
49759      */
49760     resizable : false,
49761      /**
49762      * @cfg {Number} height (in pixels)
49763      */   
49764     height: 300,
49765    /**
49766      * @cfg {Number} width (in pixels)
49767      */   
49768     width: 500,
49769      /**
49770      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49771      *         if you are doing an email editor, this probably needs disabling, it's designed
49772      */
49773     autoClean: true,
49774     
49775     /**
49776      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49777      */
49778     enableBlocks : true,
49779     /**
49780      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49781      * 
49782      */
49783     stylesheets: false,
49784      /**
49785      * @cfg {String} language default en - language of text (usefull for rtl languages)
49786      * 
49787      */
49788     language: 'en',
49789     
49790     /**
49791      * @cfg {boolean} allowComments - default false - allow comments in HTML source
49792      *          - by default they are stripped - if you are editing email you may need this.
49793      */
49794     allowComments: false,
49795     // id of frame..
49796     frameId: false,
49797     
49798     // private properties
49799     validationEvent : false,
49800     deferHeight: true,
49801     initialized : false,
49802     activated : false,
49803     sourceEditMode : false,
49804     onFocus : Roo.emptyFn,
49805     iframePad:3,
49806     hideMode:'offsets',
49807     
49808     clearUp: true,
49809     
49810     // blacklist + whitelisted elements..
49811     black: false,
49812     white: false,
49813      
49814     bodyCls : '',
49815
49816     
49817     undoManager : false,
49818     /**
49819      * Protected method that will not generally be called directly. It
49820      * is called when the editor initializes the iframe with HTML contents. Override this method if you
49821      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
49822      */
49823     getDocMarkup : function(){
49824         // body styles..
49825         var st = '';
49826         
49827         // inherit styels from page...?? 
49828         if (this.stylesheets === false) {
49829             
49830             Roo.get(document.head).select('style').each(function(node) {
49831                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49832             });
49833             
49834             Roo.get(document.head).select('link').each(function(node) { 
49835                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49836             });
49837             
49838         } else if (!this.stylesheets.length) {
49839                 // simple..
49840                 st = '<style type="text/css">' +
49841                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49842                    '</style>';
49843         } else {
49844             for (var i in this.stylesheets) {
49845                 if (typeof(this.stylesheets[i]) != 'string') {
49846                     continue;
49847                 }
49848                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
49849             }
49850             
49851         }
49852         
49853         st +=  '<style type="text/css">' +
49854             'IMG { cursor: pointer } ' +
49855         '</style>';
49856         
49857         st += '<meta name="google" content="notranslate">';
49858         
49859         var cls = 'notranslate roo-htmleditor-body';
49860         
49861         if(this.bodyCls.length){
49862             cls += ' ' + this.bodyCls;
49863         }
49864         
49865         return '<html  class="notranslate" translate="no"><head>' + st  +
49866             //<style type="text/css">' +
49867             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49868             //'</style>' +
49869             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
49870     },
49871
49872     // private
49873     onRender : function(ct, position)
49874     {
49875         var _t = this;
49876         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
49877         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
49878         
49879         
49880         this.el.dom.style.border = '0 none';
49881         this.el.dom.setAttribute('tabIndex', -1);
49882         this.el.addClass('x-hidden hide');
49883         
49884         
49885         
49886         if(Roo.isIE){ // fix IE 1px bogus margin
49887             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
49888         }
49889        
49890         
49891         this.frameId = Roo.id();
49892         
49893          
49894         
49895         var iframe = this.owner.wrap.createChild({
49896             tag: 'iframe',
49897             cls: 'form-control', // bootstrap..
49898             id: this.frameId,
49899             name: this.frameId,
49900             frameBorder : 'no',
49901             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
49902         }, this.el
49903         );
49904         
49905         
49906         this.iframe = iframe.dom;
49907
49908         this.assignDocWin();
49909         
49910         this.doc.designMode = 'on';
49911        
49912         this.doc.open();
49913         this.doc.write(this.getDocMarkup());
49914         this.doc.close();
49915
49916         
49917         var task = { // must defer to wait for browser to be ready
49918             run : function(){
49919                 //console.log("run task?" + this.doc.readyState);
49920                 this.assignDocWin();
49921                 if(this.doc.body || this.doc.readyState == 'complete'){
49922                     try {
49923                         this.doc.designMode="on";
49924                         
49925                     } catch (e) {
49926                         return;
49927                     }
49928                     Roo.TaskMgr.stop(task);
49929                     this.initEditor.defer(10, this);
49930                 }
49931             },
49932             interval : 10,
49933             duration: 10000,
49934             scope: this
49935         };
49936         Roo.TaskMgr.start(task);
49937
49938     },
49939
49940     // private
49941     onResize : function(w, h)
49942     {
49943          Roo.log('resize: ' +w + ',' + h );
49944         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
49945         if(!this.iframe){
49946             return;
49947         }
49948         if(typeof w == 'number'){
49949             
49950             this.iframe.style.width = w + 'px';
49951         }
49952         if(typeof h == 'number'){
49953             
49954             this.iframe.style.height = h + 'px';
49955             if(this.doc){
49956                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
49957             }
49958         }
49959         
49960     },
49961
49962     /**
49963      * Toggles the editor between standard and source edit mode.
49964      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49965      */
49966     toggleSourceEdit : function(sourceEditMode){
49967         
49968         this.sourceEditMode = sourceEditMode === true;
49969         
49970         if(this.sourceEditMode){
49971  
49972             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
49973             
49974         }else{
49975             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
49976             //this.iframe.className = '';
49977             this.deferFocus();
49978         }
49979         //this.setSize(this.owner.wrap.getSize());
49980         //this.fireEvent('editmodechange', this, this.sourceEditMode);
49981     },
49982
49983     
49984   
49985
49986     /**
49987      * Protected method that will not generally be called directly. If you need/want
49988      * custom HTML cleanup, this is the method you should override.
49989      * @param {String} html The HTML to be cleaned
49990      * return {String} The cleaned HTML
49991      */
49992     cleanHtml : function(html)
49993     {
49994         html = String(html);
49995         if(html.length > 5){
49996             if(Roo.isSafari){ // strip safari nonsense
49997                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
49998             }
49999         }
50000         if(html == '&nbsp;'){
50001             html = '';
50002         }
50003         return html;
50004     },
50005
50006     /**
50007      * HTML Editor -> Textarea
50008      * Protected method that will not generally be called directly. Syncs the contents
50009      * of the editor iframe with the textarea.
50010      */
50011     syncValue : function()
50012     {
50013         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50014         if(this.initialized){
50015             
50016             if (this.undoManager) {
50017                 this.undoManager.addEvent();
50018             }
50019
50020             
50021             var bd = (this.doc.body || this.doc.documentElement);
50022            
50023             
50024             var sel = this.win.getSelection();
50025             
50026             var div = document.createElement('div');
50027             div.innerHTML = bd.innerHTML;
50028             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50029             if (gtx.length > 0) {
50030                 var rm = gtx.item(0).parentNode;
50031                 rm.parentNode.removeChild(rm);
50032             }
50033             
50034            
50035             if (this.enableBlocks) {
50036                 new Roo.htmleditor.FilterBlock({ node : div });
50037             }
50038             //?? tidy?
50039             var tidy = new Roo.htmleditor.TidySerializer({
50040                 inner:  true
50041             });
50042             var html  = tidy.serialize(div);
50043             
50044             
50045             if(Roo.isSafari){
50046                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50047                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50048                 if(m && m[1]){
50049                     html = '<div style="'+m[0]+'">' + html + '</div>';
50050                 }
50051             }
50052             html = this.cleanHtml(html);
50053             // fix up the special chars.. normaly like back quotes in word...
50054             // however we do not want to do this with chinese..
50055             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50056                 
50057                 var cc = match.charCodeAt();
50058
50059                 // Get the character value, handling surrogate pairs
50060                 if (match.length == 2) {
50061                     // It's a surrogate pair, calculate the Unicode code point
50062                     var high = match.charCodeAt(0) - 0xD800;
50063                     var low  = match.charCodeAt(1) - 0xDC00;
50064                     cc = (high * 0x400) + low + 0x10000;
50065                 }  else if (
50066                     (cc >= 0x4E00 && cc < 0xA000 ) ||
50067                     (cc >= 0x3400 && cc < 0x4E00 ) ||
50068                     (cc >= 0xf900 && cc < 0xfb00 )
50069                 ) {
50070                         return match;
50071                 }  
50072          
50073                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50074                 return "&#" + cc + ";";
50075                 
50076                 
50077             });
50078             
50079             
50080              
50081             if(this.owner.fireEvent('beforesync', this, html) !== false){
50082                 this.el.dom.value = html;
50083                 this.owner.fireEvent('sync', this, html);
50084             }
50085         }
50086     },
50087
50088     /**
50089      * TEXTAREA -> EDITABLE
50090      * Protected method that will not generally be called directly. Pushes the value of the textarea
50091      * into the iframe editor.
50092      */
50093     pushValue : function()
50094     {
50095         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50096         if(this.initialized){
50097             var v = this.el.dom.value.trim();
50098             
50099             
50100             if(this.owner.fireEvent('beforepush', this, v) !== false){
50101                 var d = (this.doc.body || this.doc.documentElement);
50102                 d.innerHTML = v;
50103                  
50104                 this.el.dom.value = d.innerHTML;
50105                 this.owner.fireEvent('push', this, v);
50106             }
50107             if (this.autoClean) {
50108                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50109                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50110             }
50111             if (this.enableBlocks) {
50112                 Roo.htmleditor.Block.initAll(this.doc.body);
50113             }
50114             
50115             this.updateLanguage();
50116             
50117             var lc = this.doc.body.lastChild;
50118             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50119                 // add an extra line at the end.
50120                 this.doc.body.appendChild(this.doc.createElement('br'));
50121             }
50122             
50123             
50124         }
50125     },
50126
50127     // private
50128     deferFocus : function(){
50129         this.focus.defer(10, this);
50130     },
50131
50132     // doc'ed in Field
50133     focus : function(){
50134         if(this.win && !this.sourceEditMode){
50135             this.win.focus();
50136         }else{
50137             this.el.focus();
50138         }
50139     },
50140     
50141     assignDocWin: function()
50142     {
50143         var iframe = this.iframe;
50144         
50145          if(Roo.isIE){
50146             this.doc = iframe.contentWindow.document;
50147             this.win = iframe.contentWindow;
50148         } else {
50149 //            if (!Roo.get(this.frameId)) {
50150 //                return;
50151 //            }
50152 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50153 //            this.win = Roo.get(this.frameId).dom.contentWindow;
50154             
50155             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50156                 return;
50157             }
50158             
50159             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50160             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50161         }
50162     },
50163     
50164     // private
50165     initEditor : function(){
50166         //console.log("INIT EDITOR");
50167         this.assignDocWin();
50168         
50169         
50170         
50171         this.doc.designMode="on";
50172         this.doc.open();
50173         this.doc.write(this.getDocMarkup());
50174         this.doc.close();
50175         
50176         var dbody = (this.doc.body || this.doc.documentElement);
50177         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50178         // this copies styles from the containing element into thsi one..
50179         // not sure why we need all of this..
50180         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50181         
50182         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50183         //ss['background-attachment'] = 'fixed'; // w3c
50184         dbody.bgProperties = 'fixed'; // ie
50185         dbody.setAttribute("translate", "no");
50186         
50187         //Roo.DomHelper.applyStyles(dbody, ss);
50188         Roo.EventManager.on(this.doc, {
50189              
50190             'mouseup': this.onEditorEvent,
50191             'dblclick': this.onEditorEvent,
50192             'click': this.onEditorEvent,
50193             'keyup': this.onEditorEvent,
50194             
50195             buffer:100,
50196             scope: this
50197         });
50198         Roo.EventManager.on(this.doc, {
50199             'paste': this.onPasteEvent,
50200             scope : this
50201         });
50202         if(Roo.isGecko){
50203             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50204         }
50205         //??? needed???
50206         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50207             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50208         }
50209         this.initialized = true;
50210
50211         
50212         // initialize special key events - enter
50213         new Roo.htmleditor.KeyEnter({core : this});
50214         
50215          
50216         
50217         this.owner.fireEvent('initialize', this);
50218         this.pushValue();
50219     },
50220     // this is to prevent a href clicks resulting in a redirect?
50221    
50222     onPasteEvent : function(e,v)
50223     {
50224         // I think we better assume paste is going to be a dirty load of rubish from word..
50225         
50226         // even pasting into a 'email version' of this widget will have to clean up that mess.
50227         var cd = (e.browserEvent.clipboardData || window.clipboardData);
50228         
50229         // check what type of paste - if it's an image, then handle it differently.
50230         if (cd.files && cd.files.length > 0) {
50231             // pasting images?
50232             var urlAPI = (window.createObjectURL && window) || 
50233                 (window.URL && URL.revokeObjectURL && URL) || 
50234                 (window.webkitURL && webkitURL);
50235     
50236             var url = urlAPI.createObjectURL( cd.files[0]);
50237             this.insertAtCursor('<img src=" + url + ">');
50238             return false;
50239         }
50240         if (cd.types.indexOf('text/html') < 0 ) {
50241             return false;
50242         }
50243         var images = [];
50244         var html = cd.getData('text/html'); // clipboard event
50245         if (cd.types.indexOf('text/rtf') > -1) {
50246             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50247             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50248         }
50249         //Roo.log(images);
50250         //Roo.log(imgs);
50251         // fixme..
50252         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50253                        .map(function(g) { return g.toDataURL(); })
50254                        .filter(function(g) { return g != 'about:blank'; });
50255         
50256         
50257         html = this.cleanWordChars(html);
50258         
50259         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50260         
50261         
50262         var sn = this.getParentElement();
50263         // check if d contains a table, and prevent nesting??
50264         //Roo.log(d.getElementsByTagName('table'));
50265         //Roo.log(sn);
50266         //Roo.log(sn.closest('table'));
50267         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50268             e.preventDefault();
50269             this.insertAtCursor("You can not nest tables");
50270             //Roo.log("prevent?"); // fixme - 
50271             return false;
50272         }
50273         
50274         if (images.length > 0) {
50275             Roo.each(d.getElementsByTagName('img'), function(img, i) {
50276                 img.setAttribute('src', images[i]);
50277             });
50278         }
50279         if (this.autoClean) {
50280             new Roo.htmleditor.FilterWord({ node : d });
50281             
50282             new Roo.htmleditor.FilterStyleToTag({ node : d });
50283             new Roo.htmleditor.FilterAttributes({
50284                 node : d,
50285                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
50286                 attrib_clean : ['href', 'src' ] 
50287             });
50288             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50289             // should be fonts..
50290             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
50291             new Roo.htmleditor.FilterParagraph({ node : d });
50292             new Roo.htmleditor.FilterSpan({ node : d });
50293             new Roo.htmleditor.FilterLongBr({ node : d });
50294             new Roo.htmleditor.FilterComment({ node : d });
50295             
50296             
50297         }
50298         if (this.enableBlocks) {
50299                 
50300             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50301                 if (img.closest('figure')) { // assume!! that it's aready
50302                     return;
50303                 }
50304                 var fig  = new Roo.htmleditor.BlockFigure({
50305                     image_src  : img.src
50306                 });
50307                 fig.updateElement(img); // replace it..
50308                 
50309             });
50310         }
50311         
50312         
50313         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50314         if (this.enableBlocks) {
50315             Roo.htmleditor.Block.initAll(this.doc.body);
50316         }
50317          
50318         
50319         e.preventDefault();
50320         return false;
50321         // default behaveiour should be our local cleanup paste? (optional?)
50322         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50323         //this.owner.fireEvent('paste', e, v);
50324     },
50325     // private
50326     onDestroy : function(){
50327         
50328         
50329         
50330         if(this.rendered){
50331             
50332             //for (var i =0; i < this.toolbars.length;i++) {
50333             //    // fixme - ask toolbars for heights?
50334             //    this.toolbars[i].onDestroy();
50335            // }
50336             
50337             //this.wrap.dom.innerHTML = '';
50338             //this.wrap.remove();
50339         }
50340     },
50341
50342     // private
50343     onFirstFocus : function(){
50344         
50345         this.assignDocWin();
50346         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50347         
50348         this.activated = true;
50349          
50350     
50351         if(Roo.isGecko){ // prevent silly gecko errors
50352             this.win.focus();
50353             var s = this.win.getSelection();
50354             if(!s.focusNode || s.focusNode.nodeType != 3){
50355                 var r = s.getRangeAt(0);
50356                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50357                 r.collapse(true);
50358                 this.deferFocus();
50359             }
50360             try{
50361                 this.execCmd('useCSS', true);
50362                 this.execCmd('styleWithCSS', false);
50363             }catch(e){}
50364         }
50365         this.owner.fireEvent('activate', this);
50366     },
50367
50368     // private
50369     adjustFont: function(btn){
50370         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50371         //if(Roo.isSafari){ // safari
50372         //    adjust *= 2;
50373        // }
50374         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50375         if(Roo.isSafari){ // safari
50376             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50377             v =  (v < 10) ? 10 : v;
50378             v =  (v > 48) ? 48 : v;
50379             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50380             
50381         }
50382         
50383         
50384         v = Math.max(1, v+adjust);
50385         
50386         this.execCmd('FontSize', v  );
50387     },
50388
50389     onEditorEvent : function(e)
50390     {
50391          
50392         
50393         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50394             return; // we do not handle this.. (undo manager does..)
50395         }
50396         // in theory this detects if the last element is not a br, then we try and do that.
50397         // its so clicking in space at bottom triggers adding a br and moving the cursor.
50398         if (e &&
50399             e.target.nodeName == 'BODY' &&
50400             e.type == "mouseup" &&
50401             this.doc.body.lastChild
50402            ) {
50403             var lc = this.doc.body.lastChild;
50404             // gtx-trans is google translate plugin adding crap.
50405             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50406                 lc = lc.previousSibling;
50407             }
50408             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50409             // if last element is <BR> - then dont do anything.
50410             
50411                 var ns = this.doc.createElement('br');
50412                 this.doc.body.appendChild(ns);
50413                 range = this.doc.createRange();
50414                 range.setStartAfter(ns);
50415                 range.collapse(true);
50416                 var sel = this.win.getSelection();
50417                 sel.removeAllRanges();
50418                 sel.addRange(range);
50419             }
50420         }
50421         
50422         
50423         
50424         this.fireEditorEvent(e);
50425       //  this.updateToolbar();
50426         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50427     },
50428     
50429     fireEditorEvent: function(e)
50430     {
50431         this.owner.fireEvent('editorevent', this, e);
50432     },
50433
50434     insertTag : function(tg)
50435     {
50436         // could be a bit smarter... -> wrap the current selected tRoo..
50437         if (tg.toLowerCase() == 'span' ||
50438             tg.toLowerCase() == 'code' ||
50439             tg.toLowerCase() == 'sup' ||
50440             tg.toLowerCase() == 'sub' 
50441             ) {
50442             
50443             range = this.createRange(this.getSelection());
50444             var wrappingNode = this.doc.createElement(tg.toLowerCase());
50445             wrappingNode.appendChild(range.extractContents());
50446             range.insertNode(wrappingNode);
50447
50448             return;
50449             
50450             
50451             
50452         }
50453         this.execCmd("formatblock",   tg);
50454         this.undoManager.addEvent(); 
50455     },
50456     
50457     insertText : function(txt)
50458     {
50459         
50460         
50461         var range = this.createRange();
50462         range.deleteContents();
50463                //alert(Sender.getAttribute('label'));
50464                
50465         range.insertNode(this.doc.createTextNode(txt));
50466         this.undoManager.addEvent();
50467     } ,
50468     
50469      
50470
50471     /**
50472      * Executes a Midas editor command on the editor document and performs necessary focus and
50473      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50474      * @param {String} cmd The Midas command
50475      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50476      */
50477     relayCmd : function(cmd, value)
50478     {
50479         
50480         switch (cmd) {
50481             case 'justifyleft':
50482             case 'justifyright':
50483             case 'justifycenter':
50484                 // if we are in a cell, then we will adjust the
50485                 var n = this.getParentElement();
50486                 var td = n.closest('td');
50487                 if (td) {
50488                     var bl = Roo.htmleditor.Block.factory(td);
50489                     bl.textAlign = cmd.replace('justify','');
50490                     bl.updateElement();
50491                     this.owner.fireEvent('editorevent', this);
50492                     return;
50493                 }
50494                 this.execCmd('styleWithCSS', true); // 
50495                 break;
50496             case 'bold':
50497             case 'italic':
50498                 // if there is no selection, then we insert, and set the curson inside it..
50499                 this.execCmd('styleWithCSS', false); 
50500                 break;
50501                 
50502         
50503             default:
50504                 break;
50505         }
50506         
50507         
50508         this.win.focus();
50509         this.execCmd(cmd, value);
50510         this.owner.fireEvent('editorevent', this);
50511         //this.updateToolbar();
50512         this.owner.deferFocus();
50513     },
50514
50515     /**
50516      * Executes a Midas editor command directly on the editor document.
50517      * For visual commands, you should use {@link #relayCmd} instead.
50518      * <b>This should only be called after the editor is initialized.</b>
50519      * @param {String} cmd The Midas command
50520      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50521      */
50522     execCmd : function(cmd, value){
50523         this.doc.execCommand(cmd, false, value === undefined ? null : value);
50524         this.syncValue();
50525     },
50526  
50527  
50528    
50529     /**
50530      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50531      * to insert tRoo.
50532      * @param {String} text | dom node.. 
50533      */
50534     insertAtCursor : function(text)
50535     {
50536         
50537         if(!this.activated){
50538             return;
50539         }
50540          
50541         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50542             this.win.focus();
50543             
50544             
50545             // from jquery ui (MIT licenced)
50546             var range, node;
50547             var win = this.win;
50548             
50549             if (win.getSelection && win.getSelection().getRangeAt) {
50550                 
50551                 // delete the existing?
50552                 
50553                 this.createRange(this.getSelection()).deleteContents();
50554                 range = win.getSelection().getRangeAt(0);
50555                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50556                 range.insertNode(node);
50557                 range = range.cloneRange();
50558                 range.collapse(false);
50559                  
50560                 win.getSelection().removeAllRanges();
50561                 win.getSelection().addRange(range);
50562                 
50563                 
50564                 
50565             } else if (win.document.selection && win.document.selection.createRange) {
50566                 // no firefox support
50567                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50568                 win.document.selection.createRange().pasteHTML(txt);
50569             
50570             } else {
50571                 // no firefox support
50572                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50573                 this.execCmd('InsertHTML', txt);
50574             } 
50575             this.syncValue();
50576             
50577             this.deferFocus();
50578         }
50579     },
50580  // private
50581     mozKeyPress : function(e){
50582         if(e.ctrlKey){
50583             var c = e.getCharCode(), cmd;
50584           
50585             if(c > 0){
50586                 c = String.fromCharCode(c).toLowerCase();
50587                 switch(c){
50588                     case 'b':
50589                         cmd = 'bold';
50590                         break;
50591                     case 'i':
50592                         cmd = 'italic';
50593                         break;
50594                     
50595                     case 'u':
50596                         cmd = 'underline';
50597                         break;
50598                     
50599                     //case 'v':
50600                       //  this.cleanUpPaste.defer(100, this);
50601                       //  return;
50602                         
50603                 }
50604                 if(cmd){
50605                     
50606                     this.relayCmd(cmd);
50607                     //this.win.focus();
50608                     //this.execCmd(cmd);
50609                     //this.deferFocus();
50610                     e.preventDefault();
50611                 }
50612                 
50613             }
50614         }
50615     },
50616
50617     // private
50618     fixKeys : function(){ // load time branching for fastest keydown performance
50619         
50620         
50621         if(Roo.isIE){
50622             return function(e){
50623                 var k = e.getKey(), r;
50624                 if(k == e.TAB){
50625                     e.stopEvent();
50626                     r = this.doc.selection.createRange();
50627                     if(r){
50628                         r.collapse(true);
50629                         r.pasteHTML('&#160;&#160;&#160;&#160;');
50630                         this.deferFocus();
50631                     }
50632                     return;
50633                 }
50634                 /// this is handled by Roo.htmleditor.KeyEnter
50635                  /*
50636                 if(k == e.ENTER){
50637                     r = this.doc.selection.createRange();
50638                     if(r){
50639                         var target = r.parentElement();
50640                         if(!target || target.tagName.toLowerCase() != 'li'){
50641                             e.stopEvent();
50642                             r.pasteHTML('<br/>');
50643                             r.collapse(false);
50644                             r.select();
50645                         }
50646                     }
50647                 }
50648                 */
50649                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50650                 //    this.cleanUpPaste.defer(100, this);
50651                 //    return;
50652                 //}
50653                 
50654                 
50655             };
50656         }else if(Roo.isOpera){
50657             return function(e){
50658                 var k = e.getKey();
50659                 if(k == e.TAB){
50660                     e.stopEvent();
50661                     this.win.focus();
50662                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
50663                     this.deferFocus();
50664                 }
50665                
50666                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50667                 //    this.cleanUpPaste.defer(100, this);
50668                  //   return;
50669                 //}
50670                 
50671             };
50672         }else if(Roo.isSafari){
50673             return function(e){
50674                 var k = e.getKey();
50675                 
50676                 if(k == e.TAB){
50677                     e.stopEvent();
50678                     this.execCmd('InsertText','\t');
50679                     this.deferFocus();
50680                     return;
50681                 }
50682                  this.mozKeyPress(e);
50683                 
50684                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50685                  //   this.cleanUpPaste.defer(100, this);
50686                  //   return;
50687                // }
50688                 
50689              };
50690         }
50691     }(),
50692     
50693     getAllAncestors: function()
50694     {
50695         var p = this.getSelectedNode();
50696         var a = [];
50697         if (!p) {
50698             a.push(p); // push blank onto stack..
50699             p = this.getParentElement();
50700         }
50701         
50702         
50703         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50704             a.push(p);
50705             p = p.parentNode;
50706         }
50707         a.push(this.doc.body);
50708         return a;
50709     },
50710     lastSel : false,
50711     lastSelNode : false,
50712     
50713     
50714     getSelection : function() 
50715     {
50716         this.assignDocWin();
50717         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50718     },
50719     /**
50720      * Select a dom node
50721      * @param {DomElement} node the node to select
50722      */
50723     selectNode : function(node, collapse)
50724     {
50725         var nodeRange = node.ownerDocument.createRange();
50726         try {
50727             nodeRange.selectNode(node);
50728         } catch (e) {
50729             nodeRange.selectNodeContents(node);
50730         }
50731         if (collapse === true) {
50732             nodeRange.collapse(true);
50733         }
50734         //
50735         var s = this.win.getSelection();
50736         s.removeAllRanges();
50737         s.addRange(nodeRange);
50738     },
50739     
50740     getSelectedNode: function() 
50741     {
50742         // this may only work on Gecko!!!
50743         
50744         // should we cache this!!!!
50745         
50746          
50747          
50748         var range = this.createRange(this.getSelection()).cloneRange();
50749         
50750         if (Roo.isIE) {
50751             var parent = range.parentElement();
50752             while (true) {
50753                 var testRange = range.duplicate();
50754                 testRange.moveToElementText(parent);
50755                 if (testRange.inRange(range)) {
50756                     break;
50757                 }
50758                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
50759                     break;
50760                 }
50761                 parent = parent.parentElement;
50762             }
50763             return parent;
50764         }
50765         
50766         // is ancestor a text element.
50767         var ac =  range.commonAncestorContainer;
50768         if (ac.nodeType == 3) {
50769             ac = ac.parentNode;
50770         }
50771         
50772         var ar = ac.childNodes;
50773          
50774         var nodes = [];
50775         var other_nodes = [];
50776         var has_other_nodes = false;
50777         for (var i=0;i<ar.length;i++) {
50778             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
50779                 continue;
50780             }
50781             // fullly contained node.
50782             
50783             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
50784                 nodes.push(ar[i]);
50785                 continue;
50786             }
50787             
50788             // probably selected..
50789             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
50790                 other_nodes.push(ar[i]);
50791                 continue;
50792             }
50793             // outer..
50794             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
50795                 continue;
50796             }
50797             
50798             
50799             has_other_nodes = true;
50800         }
50801         if (!nodes.length && other_nodes.length) {
50802             nodes= other_nodes;
50803         }
50804         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
50805             return false;
50806         }
50807         
50808         return nodes[0];
50809     },
50810     
50811     
50812     createRange: function(sel)
50813     {
50814         // this has strange effects when using with 
50815         // top toolbar - not sure if it's a great idea.
50816         //this.editor.contentWindow.focus();
50817         if (typeof sel != "undefined") {
50818             try {
50819                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
50820             } catch(e) {
50821                 return this.doc.createRange();
50822             }
50823         } else {
50824             return this.doc.createRange();
50825         }
50826     },
50827     getParentElement: function()
50828     {
50829         
50830         this.assignDocWin();
50831         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
50832         
50833         var range = this.createRange(sel);
50834          
50835         try {
50836             var p = range.commonAncestorContainer;
50837             while (p.nodeType == 3) { // text node
50838                 p = p.parentNode;
50839             }
50840             return p;
50841         } catch (e) {
50842             return null;
50843         }
50844     
50845     },
50846     /***
50847      *
50848      * Range intersection.. the hard stuff...
50849      *  '-1' = before
50850      *  '0' = hits..
50851      *  '1' = after.
50852      *         [ -- selected range --- ]
50853      *   [fail]                        [fail]
50854      *
50855      *    basically..
50856      *      if end is before start or  hits it. fail.
50857      *      if start is after end or hits it fail.
50858      *
50859      *   if either hits (but other is outside. - then it's not 
50860      *   
50861      *    
50862      **/
50863     
50864     
50865     // @see http://www.thismuchiknow.co.uk/?p=64.
50866     rangeIntersectsNode : function(range, node)
50867     {
50868         var nodeRange = node.ownerDocument.createRange();
50869         try {
50870             nodeRange.selectNode(node);
50871         } catch (e) {
50872             nodeRange.selectNodeContents(node);
50873         }
50874     
50875         var rangeStartRange = range.cloneRange();
50876         rangeStartRange.collapse(true);
50877     
50878         var rangeEndRange = range.cloneRange();
50879         rangeEndRange.collapse(false);
50880     
50881         var nodeStartRange = nodeRange.cloneRange();
50882         nodeStartRange.collapse(true);
50883     
50884         var nodeEndRange = nodeRange.cloneRange();
50885         nodeEndRange.collapse(false);
50886     
50887         return rangeStartRange.compareBoundaryPoints(
50888                  Range.START_TO_START, nodeEndRange) == -1 &&
50889                rangeEndRange.compareBoundaryPoints(
50890                  Range.START_TO_START, nodeStartRange) == 1;
50891         
50892          
50893     },
50894     rangeCompareNode : function(range, node)
50895     {
50896         var nodeRange = node.ownerDocument.createRange();
50897         try {
50898             nodeRange.selectNode(node);
50899         } catch (e) {
50900             nodeRange.selectNodeContents(node);
50901         }
50902         
50903         
50904         range.collapse(true);
50905     
50906         nodeRange.collapse(true);
50907      
50908         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
50909         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
50910          
50911         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
50912         
50913         var nodeIsBefore   =  ss == 1;
50914         var nodeIsAfter    = ee == -1;
50915         
50916         if (nodeIsBefore && nodeIsAfter) {
50917             return 0; // outer
50918         }
50919         if (!nodeIsBefore && nodeIsAfter) {
50920             return 1; //right trailed.
50921         }
50922         
50923         if (nodeIsBefore && !nodeIsAfter) {
50924             return 2;  // left trailed.
50925         }
50926         // fully contined.
50927         return 3;
50928     },
50929  
50930     cleanWordChars : function(input) {// change the chars to hex code
50931         
50932        var swapCodes  = [ 
50933             [    8211, "&#8211;" ], 
50934             [    8212, "&#8212;" ], 
50935             [    8216,  "'" ],  
50936             [    8217, "'" ],  
50937             [    8220, '"' ],  
50938             [    8221, '"' ],  
50939             [    8226, "*" ],  
50940             [    8230, "..." ]
50941         ]; 
50942         var output = input;
50943         Roo.each(swapCodes, function(sw) { 
50944             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
50945             
50946             output = output.replace(swapper, sw[1]);
50947         });
50948         
50949         return output;
50950     },
50951     
50952      
50953     
50954         
50955     
50956     cleanUpChild : function (node)
50957     {
50958         
50959         new Roo.htmleditor.FilterComment({node : node});
50960         new Roo.htmleditor.FilterAttributes({
50961                 node : node,
50962                 attrib_black : this.ablack,
50963                 attrib_clean : this.aclean,
50964                 style_white : this.cwhite,
50965                 style_black : this.cblack
50966         });
50967         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
50968         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
50969          
50970         
50971     },
50972     
50973     /**
50974      * Clean up MS wordisms...
50975      * @deprecated - use filter directly
50976      */
50977     cleanWord : function(node)
50978     {
50979         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
50980         
50981     },
50982    
50983     
50984     /**
50985
50986      * @deprecated - use filters
50987      */
50988     cleanTableWidths : function(node)
50989     {
50990         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
50991         
50992  
50993     },
50994     
50995      
50996         
50997     applyBlacklists : function()
50998     {
50999         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
51000         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
51001         
51002         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
51003         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
51004         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
51005         
51006         this.white = [];
51007         this.black = [];
51008         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51009             if (b.indexOf(tag) > -1) {
51010                 return;
51011             }
51012             this.white.push(tag);
51013             
51014         }, this);
51015         
51016         Roo.each(w, function(tag) {
51017             if (b.indexOf(tag) > -1) {
51018                 return;
51019             }
51020             if (this.white.indexOf(tag) > -1) {
51021                 return;
51022             }
51023             this.white.push(tag);
51024             
51025         }, this);
51026         
51027         
51028         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51029             if (w.indexOf(tag) > -1) {
51030                 return;
51031             }
51032             this.black.push(tag);
51033             
51034         }, this);
51035         
51036         Roo.each(b, function(tag) {
51037             if (w.indexOf(tag) > -1) {
51038                 return;
51039             }
51040             if (this.black.indexOf(tag) > -1) {
51041                 return;
51042             }
51043             this.black.push(tag);
51044             
51045         }, this);
51046         
51047         
51048         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
51049         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
51050         
51051         this.cwhite = [];
51052         this.cblack = [];
51053         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51054             if (b.indexOf(tag) > -1) {
51055                 return;
51056             }
51057             this.cwhite.push(tag);
51058             
51059         }, this);
51060         
51061         Roo.each(w, function(tag) {
51062             if (b.indexOf(tag) > -1) {
51063                 return;
51064             }
51065             if (this.cwhite.indexOf(tag) > -1) {
51066                 return;
51067             }
51068             this.cwhite.push(tag);
51069             
51070         }, this);
51071         
51072         
51073         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51074             if (w.indexOf(tag) > -1) {
51075                 return;
51076             }
51077             this.cblack.push(tag);
51078             
51079         }, this);
51080         
51081         Roo.each(b, function(tag) {
51082             if (w.indexOf(tag) > -1) {
51083                 return;
51084             }
51085             if (this.cblack.indexOf(tag) > -1) {
51086                 return;
51087             }
51088             this.cblack.push(tag);
51089             
51090         }, this);
51091     },
51092     
51093     setStylesheets : function(stylesheets)
51094     {
51095         if(typeof(stylesheets) == 'string'){
51096             Roo.get(this.iframe.contentDocument.head).createChild({
51097                 tag : 'link',
51098                 rel : 'stylesheet',
51099                 type : 'text/css',
51100                 href : stylesheets
51101             });
51102             
51103             return;
51104         }
51105         var _this = this;
51106      
51107         Roo.each(stylesheets, function(s) {
51108             if(!s.length){
51109                 return;
51110             }
51111             
51112             Roo.get(_this.iframe.contentDocument.head).createChild({
51113                 tag : 'link',
51114                 rel : 'stylesheet',
51115                 type : 'text/css',
51116                 href : s
51117             });
51118         });
51119
51120         
51121     },
51122     
51123     
51124     updateLanguage : function()
51125     {
51126         if (!this.iframe || !this.iframe.contentDocument) {
51127             return;
51128         }
51129         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51130     },
51131     
51132     
51133     removeStylesheets : function()
51134     {
51135         var _this = this;
51136         
51137         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51138             s.remove();
51139         });
51140     },
51141     
51142     setStyle : function(style)
51143     {
51144         Roo.get(this.iframe.contentDocument.head).createChild({
51145             tag : 'style',
51146             type : 'text/css',
51147             html : style
51148         });
51149
51150         return;
51151     }
51152     
51153     // hide stuff that is not compatible
51154     /**
51155      * @event blur
51156      * @hide
51157      */
51158     /**
51159      * @event change
51160      * @hide
51161      */
51162     /**
51163      * @event focus
51164      * @hide
51165      */
51166     /**
51167      * @event specialkey
51168      * @hide
51169      */
51170     /**
51171      * @cfg {String} fieldClass @hide
51172      */
51173     /**
51174      * @cfg {String} focusClass @hide
51175      */
51176     /**
51177      * @cfg {String} autoCreate @hide
51178      */
51179     /**
51180      * @cfg {String} inputType @hide
51181      */
51182     /**
51183      * @cfg {String} invalidClass @hide
51184      */
51185     /**
51186      * @cfg {String} invalidText @hide
51187      */
51188     /**
51189      * @cfg {String} msgFx @hide
51190      */
51191     /**
51192      * @cfg {String} validateOnBlur @hide
51193      */
51194 });
51195
51196 Roo.HtmlEditorCore.white = [
51197         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51198         
51199        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
51200        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
51201        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
51202        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
51203        'TABLE',   'UL',         'XMP', 
51204        
51205        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
51206       'THEAD',   'TR', 
51207      
51208       'DIR', 'MENU', 'OL', 'UL', 'DL',
51209        
51210       'EMBED',  'OBJECT'
51211 ];
51212
51213
51214 Roo.HtmlEditorCore.black = [
51215     //    'embed',  'object', // enable - backend responsiblity to clean thiese
51216         'APPLET', // 
51217         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
51218         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
51219         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
51220         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
51221         //'FONT' // CLEAN LATER..
51222         'COLGROUP', 'COL'   // messy tables.
51223         
51224         
51225 ];
51226 Roo.HtmlEditorCore.clean = [ // ?? needed???
51227      'SCRIPT', 'STYLE', 'TITLE', 'XML'
51228 ];
51229 Roo.HtmlEditorCore.tag_remove = [
51230     'FONT', 'TBODY'  
51231 ];
51232 // attributes..
51233
51234 Roo.HtmlEditorCore.ablack = [
51235     'on'
51236 ];
51237     
51238 Roo.HtmlEditorCore.aclean = [ 
51239     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
51240 ];
51241
51242 // protocols..
51243 Roo.HtmlEditorCore.pwhite= [
51244         'http',  'https',  'mailto'
51245 ];
51246
51247 // white listed style attributes.
51248 Roo.HtmlEditorCore.cwhite= [
51249       //  'text-align', /// default is to allow most things..
51250       
51251          
51252 //        'font-size'//??
51253 ];
51254
51255 // black listed style attributes.
51256 Roo.HtmlEditorCore.cblack= [
51257       //  'font-size' -- this can be set by the project 
51258 ];
51259
51260
51261
51262
51263     //<script type="text/javascript">
51264
51265 /*
51266  * Ext JS Library 1.1.1
51267  * Copyright(c) 2006-2007, Ext JS, LLC.
51268  * Licence LGPL
51269  * 
51270  */
51271  
51272  
51273 Roo.form.HtmlEditor = function(config){
51274     
51275     
51276     
51277     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51278     
51279     if (!this.toolbars) {
51280         this.toolbars = [];
51281     }
51282     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51283     
51284     
51285 };
51286
51287 /**
51288  * @class Roo.form.HtmlEditor
51289  * @extends Roo.form.Field
51290  * Provides a lightweight HTML Editor component.
51291  *
51292  * This has been tested on Fireforx / Chrome.. IE may not be so great..
51293  * 
51294  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51295  * supported by this editor.</b><br/><br/>
51296  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51297  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51298  */
51299 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51300     /**
51301      * @cfg {Boolean} clearUp
51302      */
51303     clearUp : true,
51304       /**
51305      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51306      */
51307     toolbars : false,
51308    
51309      /**
51310      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
51311      *                        Roo.resizable.
51312      */
51313     resizable : false,
51314      /**
51315      * @cfg {Number} height (in pixels)
51316      */   
51317     height: 300,
51318    /**
51319      * @cfg {Number} width (in pixels)
51320      */   
51321     width: 500,
51322     
51323     /**
51324      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
51325      * 
51326      */
51327     stylesheets: false,
51328     
51329     
51330      /**
51331      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51332      * 
51333      */
51334     cblack: false,
51335     /**
51336      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51337      * 
51338      */
51339     cwhite: false,
51340     
51341      /**
51342      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51343      * 
51344      */
51345     black: false,
51346     /**
51347      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51348      * 
51349      */
51350     white: false,
51351     /**
51352      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51353      */
51354     allowComments: false,
51355     /**
51356      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51357      */
51358     enableBlocks : true,
51359     
51360     /**
51361      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51362      *         if you are doing an email editor, this probably needs disabling, it's designed
51363      */
51364     autoClean: true,
51365     /**
51366      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51367      */
51368     bodyCls : '',
51369     /**
51370      * @cfg {String} language default en - language of text (usefull for rtl languages)
51371      * 
51372      */
51373     language: 'en',
51374     
51375      
51376     // id of frame..
51377     frameId: false,
51378     
51379     // private properties
51380     validationEvent : false,
51381     deferHeight: true,
51382     initialized : false,
51383     activated : false,
51384     
51385     onFocus : Roo.emptyFn,
51386     iframePad:3,
51387     hideMode:'offsets',
51388     
51389     actionMode : 'container', // defaults to hiding it...
51390     
51391     defaultAutoCreate : { // modified by initCompnoent..
51392         tag: "textarea",
51393         style:"width:500px;height:300px;",
51394         autocomplete: "new-password"
51395     },
51396
51397     // private
51398     initComponent : function(){
51399         this.addEvents({
51400             /**
51401              * @event initialize
51402              * Fires when the editor is fully initialized (including the iframe)
51403              * @param {HtmlEditor} this
51404              */
51405             initialize: true,
51406             /**
51407              * @event activate
51408              * Fires when the editor is first receives the focus. Any insertion must wait
51409              * until after this event.
51410              * @param {HtmlEditor} this
51411              */
51412             activate: true,
51413              /**
51414              * @event beforesync
51415              * Fires before the textarea is updated with content from the editor iframe. Return false
51416              * to cancel the sync.
51417              * @param {HtmlEditor} this
51418              * @param {String} html
51419              */
51420             beforesync: true,
51421              /**
51422              * @event beforepush
51423              * Fires before the iframe editor is updated with content from the textarea. Return false
51424              * to cancel the push.
51425              * @param {HtmlEditor} this
51426              * @param {String} html
51427              */
51428             beforepush: true,
51429              /**
51430              * @event sync
51431              * Fires when the textarea is updated with content from the editor iframe.
51432              * @param {HtmlEditor} this
51433              * @param {String} html
51434              */
51435             sync: true,
51436              /**
51437              * @event push
51438              * Fires when the iframe editor is updated with content from the textarea.
51439              * @param {HtmlEditor} this
51440              * @param {String} html
51441              */
51442             push: true,
51443              /**
51444              * @event editmodechange
51445              * Fires when the editor switches edit modes
51446              * @param {HtmlEditor} this
51447              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51448              */
51449             editmodechange: true,
51450             /**
51451              * @event editorevent
51452              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51453              * @param {HtmlEditor} this
51454              */
51455             editorevent: true,
51456             /**
51457              * @event firstfocus
51458              * Fires when on first focus - needed by toolbars..
51459              * @param {HtmlEditor} this
51460              */
51461             firstfocus: true,
51462             /**
51463              * @event autosave
51464              * Auto save the htmlEditor value as a file into Events
51465              * @param {HtmlEditor} this
51466              */
51467             autosave: true,
51468             /**
51469              * @event savedpreview
51470              * preview the saved version of htmlEditor
51471              * @param {HtmlEditor} this
51472              */
51473             savedpreview: true,
51474             
51475             /**
51476             * @event stylesheetsclick
51477             * Fires when press the Sytlesheets button
51478             * @param {Roo.HtmlEditorCore} this
51479             */
51480             stylesheetsclick: true,
51481             /**
51482             * @event paste
51483             * Fires when press user pastes into the editor
51484             * @param {Roo.HtmlEditorCore} this
51485             */
51486             paste: true 
51487         });
51488         this.defaultAutoCreate =  {
51489             tag: "textarea",
51490             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51491             autocomplete: "new-password"
51492         };
51493     },
51494
51495     /**
51496      * Protected method that will not generally be called directly. It
51497      * is called when the editor creates its toolbar. Override this method if you need to
51498      * add custom toolbar buttons.
51499      * @param {HtmlEditor} editor
51500      */
51501     createToolbar : function(editor){
51502         Roo.log("create toolbars");
51503         if (!editor.toolbars || !editor.toolbars.length) {
51504             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51505         }
51506         
51507         for (var i =0 ; i < editor.toolbars.length;i++) {
51508             editor.toolbars[i] = Roo.factory(
51509                     typeof(editor.toolbars[i]) == 'string' ?
51510                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
51511                 Roo.form.HtmlEditor);
51512             editor.toolbars[i].init(editor);
51513         }
51514          
51515         
51516     },
51517     /**
51518      * get the Context selected node
51519      * @returns {DomElement|boolean} selected node if active or false if none
51520      * 
51521      */
51522     getSelectedNode : function()
51523     {
51524         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51525             return false;
51526         }
51527         return this.toolbars[1].tb.selectedNode;
51528     
51529     },
51530     // private
51531     onRender : function(ct, position)
51532     {
51533         var _t = this;
51534         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51535         
51536         this.wrap = this.el.wrap({
51537             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51538         });
51539         
51540         this.editorcore.onRender(ct, position);
51541          
51542         if (this.resizable) {
51543             this.resizeEl = new Roo.Resizable(this.wrap, {
51544                 pinned : true,
51545                 wrap: true,
51546                 dynamic : true,
51547                 minHeight : this.height,
51548                 height: this.height,
51549                 handles : this.resizable,
51550                 width: this.width,
51551                 listeners : {
51552                     resize : function(r, w, h) {
51553                         _t.onResize(w,h); // -something
51554                     }
51555                 }
51556             });
51557             
51558         }
51559         this.createToolbar(this);
51560        
51561         
51562         if(!this.width){
51563             this.setSize(this.wrap.getSize());
51564         }
51565         if (this.resizeEl) {
51566             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51567             // should trigger onReize..
51568         }
51569         
51570         this.keyNav = new Roo.KeyNav(this.el, {
51571             
51572             "tab" : function(e){
51573                 e.preventDefault();
51574                 
51575                 var value = this.getValue();
51576                 
51577                 var start = this.el.dom.selectionStart;
51578                 var end = this.el.dom.selectionEnd;
51579                 
51580                 if(!e.shiftKey){
51581                     
51582                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51583                     this.el.dom.setSelectionRange(end + 1, end + 1);
51584                     return;
51585                 }
51586                 
51587                 var f = value.substring(0, start).split("\t");
51588                 
51589                 if(f.pop().length != 0){
51590                     return;
51591                 }
51592                 
51593                 this.setValue(f.join("\t") + value.substring(end));
51594                 this.el.dom.setSelectionRange(start - 1, start - 1);
51595                 
51596             },
51597             
51598             "home" : function(e){
51599                 e.preventDefault();
51600                 
51601                 var curr = this.el.dom.selectionStart;
51602                 var lines = this.getValue().split("\n");
51603                 
51604                 if(!lines.length){
51605                     return;
51606                 }
51607                 
51608                 if(e.ctrlKey){
51609                     this.el.dom.setSelectionRange(0, 0);
51610                     return;
51611                 }
51612                 
51613                 var pos = 0;
51614                 
51615                 for (var i = 0; i < lines.length;i++) {
51616                     pos += lines[i].length;
51617                     
51618                     if(i != 0){
51619                         pos += 1;
51620                     }
51621                     
51622                     if(pos < curr){
51623                         continue;
51624                     }
51625                     
51626                     pos -= lines[i].length;
51627                     
51628                     break;
51629                 }
51630                 
51631                 if(!e.shiftKey){
51632                     this.el.dom.setSelectionRange(pos, pos);
51633                     return;
51634                 }
51635                 
51636                 this.el.dom.selectionStart = pos;
51637                 this.el.dom.selectionEnd = curr;
51638             },
51639             
51640             "end" : function(e){
51641                 e.preventDefault();
51642                 
51643                 var curr = this.el.dom.selectionStart;
51644                 var lines = this.getValue().split("\n");
51645                 
51646                 if(!lines.length){
51647                     return;
51648                 }
51649                 
51650                 if(e.ctrlKey){
51651                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51652                     return;
51653                 }
51654                 
51655                 var pos = 0;
51656                 
51657                 for (var i = 0; i < lines.length;i++) {
51658                     
51659                     pos += lines[i].length;
51660                     
51661                     if(i != 0){
51662                         pos += 1;
51663                     }
51664                     
51665                     if(pos < curr){
51666                         continue;
51667                     }
51668                     
51669                     break;
51670                 }
51671                 
51672                 if(!e.shiftKey){
51673                     this.el.dom.setSelectionRange(pos, pos);
51674                     return;
51675                 }
51676                 
51677                 this.el.dom.selectionStart = curr;
51678                 this.el.dom.selectionEnd = pos;
51679             },
51680
51681             scope : this,
51682
51683             doRelay : function(foo, bar, hname){
51684                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51685             },
51686
51687             forceKeyDown: true
51688         });
51689         
51690 //        if(this.autosave && this.w){
51691 //            this.autoSaveFn = setInterval(this.autosave, 1000);
51692 //        }
51693     },
51694
51695     // private
51696     onResize : function(w, h)
51697     {
51698         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51699         var ew = false;
51700         var eh = false;
51701         
51702         if(this.el ){
51703             if(typeof w == 'number'){
51704                 var aw = w - this.wrap.getFrameWidth('lr');
51705                 this.el.setWidth(this.adjustWidth('textarea', aw));
51706                 ew = aw;
51707             }
51708             if(typeof h == 'number'){
51709                 var tbh = 0;
51710                 for (var i =0; i < this.toolbars.length;i++) {
51711                     // fixme - ask toolbars for heights?
51712                     tbh += this.toolbars[i].tb.el.getHeight();
51713                     if (this.toolbars[i].footer) {
51714                         tbh += this.toolbars[i].footer.el.getHeight();
51715                     }
51716                 }
51717                 
51718                 
51719                 
51720                 
51721                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51722                 ah -= 5; // knock a few pixes off for look..
51723 //                Roo.log(ah);
51724                 this.el.setHeight(this.adjustWidth('textarea', ah));
51725                 var eh = ah;
51726             }
51727         }
51728         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51729         this.editorcore.onResize(ew,eh);
51730         
51731     },
51732
51733     /**
51734      * Toggles the editor between standard and source edit mode.
51735      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51736      */
51737     toggleSourceEdit : function(sourceEditMode)
51738     {
51739         this.editorcore.toggleSourceEdit(sourceEditMode);
51740         
51741         if(this.editorcore.sourceEditMode){
51742             Roo.log('editor - showing textarea');
51743             
51744 //            Roo.log('in');
51745 //            Roo.log(this.syncValue());
51746             this.editorcore.syncValue();
51747             this.el.removeClass('x-hidden');
51748             this.el.dom.removeAttribute('tabIndex');
51749             this.el.focus();
51750             this.el.dom.scrollTop = 0;
51751             
51752             
51753             for (var i = 0; i < this.toolbars.length; i++) {
51754                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51755                     this.toolbars[i].tb.hide();
51756                     this.toolbars[i].footer.hide();
51757                 }
51758             }
51759             
51760         }else{
51761             Roo.log('editor - hiding textarea');
51762 //            Roo.log('out')
51763 //            Roo.log(this.pushValue()); 
51764             this.editorcore.pushValue();
51765             
51766             this.el.addClass('x-hidden');
51767             this.el.dom.setAttribute('tabIndex', -1);
51768             
51769             for (var i = 0; i < this.toolbars.length; i++) {
51770                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51771                     this.toolbars[i].tb.show();
51772                     this.toolbars[i].footer.show();
51773                 }
51774             }
51775             
51776             //this.deferFocus();
51777         }
51778         
51779         this.setSize(this.wrap.getSize());
51780         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
51781         
51782         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
51783     },
51784  
51785     // private (for BoxComponent)
51786     adjustSize : Roo.BoxComponent.prototype.adjustSize,
51787
51788     // private (for BoxComponent)
51789     getResizeEl : function(){
51790         return this.wrap;
51791     },
51792
51793     // private (for BoxComponent)
51794     getPositionEl : function(){
51795         return this.wrap;
51796     },
51797
51798     // private
51799     initEvents : function(){
51800         this.originalValue = this.getValue();
51801     },
51802
51803     /**
51804      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51805      * @method
51806      */
51807     markInvalid : Roo.emptyFn,
51808     /**
51809      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51810      * @method
51811      */
51812     clearInvalid : Roo.emptyFn,
51813
51814     setValue : function(v){
51815         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
51816         this.editorcore.pushValue();
51817     },
51818
51819     /**
51820      * update the language in the body - really done by core
51821      * @param {String} language - eg. en / ar / zh-CN etc..
51822      */
51823     updateLanguage : function(lang)
51824     {
51825         this.language = lang;
51826         this.editorcore.language = lang;
51827         this.editorcore.updateLanguage();
51828      
51829     },
51830     // private
51831     deferFocus : function(){
51832         this.focus.defer(10, this);
51833     },
51834
51835     // doc'ed in Field
51836     focus : function(){
51837         this.editorcore.focus();
51838         
51839     },
51840       
51841
51842     // private
51843     onDestroy : function(){
51844         
51845         
51846         
51847         if(this.rendered){
51848             
51849             for (var i =0; i < this.toolbars.length;i++) {
51850                 // fixme - ask toolbars for heights?
51851                 this.toolbars[i].onDestroy();
51852             }
51853             
51854             this.wrap.dom.innerHTML = '';
51855             this.wrap.remove();
51856         }
51857     },
51858
51859     // private
51860     onFirstFocus : function(){
51861         //Roo.log("onFirstFocus");
51862         this.editorcore.onFirstFocus();
51863          for (var i =0; i < this.toolbars.length;i++) {
51864             this.toolbars[i].onFirstFocus();
51865         }
51866         
51867     },
51868     
51869     // private
51870     syncValue : function()
51871     {
51872         this.editorcore.syncValue();
51873     },
51874     
51875     pushValue : function()
51876     {
51877         this.editorcore.pushValue();
51878     },
51879     
51880     setStylesheets : function(stylesheets)
51881     {
51882         this.editorcore.setStylesheets(stylesheets);
51883     },
51884     
51885     removeStylesheets : function()
51886     {
51887         this.editorcore.removeStylesheets();
51888     }
51889      
51890     
51891     // hide stuff that is not compatible
51892     /**
51893      * @event blur
51894      * @hide
51895      */
51896     /**
51897      * @event change
51898      * @hide
51899      */
51900     /**
51901      * @event focus
51902      * @hide
51903      */
51904     /**
51905      * @event specialkey
51906      * @hide
51907      */
51908     /**
51909      * @cfg {String} fieldClass @hide
51910      */
51911     /**
51912      * @cfg {String} focusClass @hide
51913      */
51914     /**
51915      * @cfg {String} autoCreate @hide
51916      */
51917     /**
51918      * @cfg {String} inputType @hide
51919      */
51920     /**
51921      * @cfg {String} invalidClass @hide
51922      */
51923     /**
51924      * @cfg {String} invalidText @hide
51925      */
51926     /**
51927      * @cfg {String} msgFx @hide
51928      */
51929     /**
51930      * @cfg {String} validateOnBlur @hide
51931      */
51932 });
51933  
51934     /*
51935  * Based on
51936  * Ext JS Library 1.1.1
51937  * Copyright(c) 2006-2007, Ext JS, LLC.
51938  *  
51939  
51940  */
51941
51942 /**
51943  * @class Roo.form.HtmlEditor.ToolbarStandard
51944  * Basic Toolbar
51945
51946  * Usage:
51947  *
51948  new Roo.form.HtmlEditor({
51949     ....
51950     toolbars : [
51951         new Roo.form.HtmlEditorToolbar1({
51952             disable : { fonts: 1 , format: 1, ..., ... , ...],
51953             btns : [ .... ]
51954         })
51955     }
51956      
51957  * 
51958  * @cfg {Object} disable List of elements to disable..
51959  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
51960  * 
51961  * 
51962  * NEEDS Extra CSS? 
51963  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
51964  */
51965  
51966 Roo.form.HtmlEditor.ToolbarStandard = function(config)
51967 {
51968     
51969     Roo.apply(this, config);
51970     
51971     // default disabled, based on 'good practice'..
51972     this.disable = this.disable || {};
51973     Roo.applyIf(this.disable, {
51974         fontSize : true,
51975         colors : true,
51976         specialElements : true
51977     });
51978     
51979     
51980     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
51981     // dont call parent... till later.
51982 }
51983
51984 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
51985     
51986     tb: false,
51987     
51988     rendered: false,
51989     
51990     editor : false,
51991     editorcore : false,
51992     /**
51993      * @cfg {Object} disable  List of toolbar elements to disable
51994          
51995      */
51996     disable : false,
51997     
51998     
51999      /**
52000      * @cfg {String} createLinkText The default text for the create link prompt
52001      */
52002     createLinkText : 'Please enter the URL for the link:',
52003     /**
52004      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52005      */
52006     defaultLinkValue : 'http:/'+'/',
52007    
52008     
52009       /**
52010      * @cfg {Array} fontFamilies An array of available font families
52011      */
52012     fontFamilies : [
52013         'Arial',
52014         'Courier New',
52015         'Tahoma',
52016         'Times New Roman',
52017         'Verdana'
52018     ],
52019     
52020     specialChars : [
52021            "&#169;",
52022           "&#174;",     
52023           "&#8482;",    
52024           "&#163;" ,    
52025          // "&#8212;",    
52026           "&#8230;",    
52027           "&#247;" ,    
52028         //  "&#225;" ,     ?? a acute?
52029            "&#8364;"    , //Euro
52030        //   "&#8220;"    ,
52031         //  "&#8221;"    ,
52032         //  "&#8226;"    ,
52033           "&#176;"  //   , // degrees
52034
52035          // "&#233;"     , // e ecute
52036          // "&#250;"     , // u ecute?
52037     ],
52038     
52039     specialElements : [
52040         {
52041             text: "Insert Table",
52042             xtype: 'MenuItem',
52043             xns : Roo.Menu,
52044             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
52045                 
52046         },
52047         {    
52048             text: "Insert Image",
52049             xtype: 'MenuItem',
52050             xns : Roo.Menu,
52051             ihtml : '<img src="about:blank"/>'
52052             
52053         }
52054         
52055          
52056     ],
52057     
52058     
52059     inputElements : [ 
52060             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
52061             "input:submit", "input:button", "select", "textarea", "label" ],
52062     formats : [
52063         ["p"] ,  
52064         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
52065         ["pre"],[ "code"], 
52066         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52067         ['div'],['span'],
52068         ['sup'],['sub']
52069     ],
52070     
52071     cleanStyles : [
52072         "font-size"
52073     ],
52074      /**
52075      * @cfg {String} defaultFont default font to use.
52076      */
52077     defaultFont: 'tahoma',
52078    
52079     fontSelect : false,
52080     
52081     
52082     formatCombo : false,
52083     
52084     init : function(editor)
52085     {
52086         this.editor = editor;
52087         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52088         var editorcore = this.editorcore;
52089         
52090         var _t = this;
52091         
52092         var fid = editorcore.frameId;
52093         var etb = this;
52094         function btn(id, toggle, handler){
52095             var xid = fid + '-'+ id ;
52096             return {
52097                 id : xid,
52098                 cmd : id,
52099                 cls : 'x-btn-icon x-edit-'+id,
52100                 enableToggle:toggle !== false,
52101                 scope: _t, // was editor...
52102                 handler:handler||_t.relayBtnCmd,
52103                 clickEvent:'mousedown',
52104                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52105                 tabIndex:-1
52106             };
52107         }
52108         
52109         
52110         
52111         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52112         this.tb = tb;
52113          // stop form submits
52114         tb.el.on('click', function(e){
52115             e.preventDefault(); // what does this do?
52116         });
52117
52118         if(!this.disable.font) { // && !Roo.isSafari){
52119             /* why no safari for fonts 
52120             editor.fontSelect = tb.el.createChild({
52121                 tag:'select',
52122                 tabIndex: -1,
52123                 cls:'x-font-select',
52124                 html: this.createFontOptions()
52125             });
52126             
52127             editor.fontSelect.on('change', function(){
52128                 var font = editor.fontSelect.dom.value;
52129                 editor.relayCmd('fontname', font);
52130                 editor.deferFocus();
52131             }, editor);
52132             
52133             tb.add(
52134                 editor.fontSelect.dom,
52135                 '-'
52136             );
52137             */
52138             
52139         };
52140         if(!this.disable.formats){
52141             this.formatCombo = new Roo.form.ComboBox({
52142                 store: new Roo.data.SimpleStore({
52143                     id : 'tag',
52144                     fields: ['tag'],
52145                     data : this.formats // from states.js
52146                 }),
52147                 blockFocus : true,
52148                 name : '',
52149                 //autoCreate : {tag: "div",  size: "20"},
52150                 displayField:'tag',
52151                 typeAhead: false,
52152                 mode: 'local',
52153                 editable : false,
52154                 triggerAction: 'all',
52155                 emptyText:'Add tag',
52156                 selectOnFocus:true,
52157                 width:135,
52158                 listeners : {
52159                     'select': function(c, r, i) {
52160                         editorcore.insertTag(r.get('tag'));
52161                         editor.focus();
52162                     }
52163                 }
52164
52165             });
52166             tb.addField(this.formatCombo);
52167             
52168         }
52169         
52170         if(!this.disable.format){
52171             tb.add(
52172                 btn('bold'),
52173                 btn('italic'),
52174                 btn('underline'),
52175                 btn('strikethrough')
52176             );
52177         };
52178         if(!this.disable.fontSize){
52179             tb.add(
52180                 '-',
52181                 
52182                 
52183                 btn('increasefontsize', false, editorcore.adjustFont),
52184                 btn('decreasefontsize', false, editorcore.adjustFont)
52185             );
52186         };
52187         
52188         
52189         if(!this.disable.colors){
52190             tb.add(
52191                 '-', {
52192                     id:editorcore.frameId +'-forecolor',
52193                     cls:'x-btn-icon x-edit-forecolor',
52194                     clickEvent:'mousedown',
52195                     tooltip: this.buttonTips['forecolor'] || undefined,
52196                     tabIndex:-1,
52197                     menu : new Roo.menu.ColorMenu({
52198                         allowReselect: true,
52199                         focus: Roo.emptyFn,
52200                         value:'000000',
52201                         plain:true,
52202                         selectHandler: function(cp, color){
52203                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52204                             editor.deferFocus();
52205                         },
52206                         scope: editorcore,
52207                         clickEvent:'mousedown'
52208                     })
52209                 }, {
52210                     id:editorcore.frameId +'backcolor',
52211                     cls:'x-btn-icon x-edit-backcolor',
52212                     clickEvent:'mousedown',
52213                     tooltip: this.buttonTips['backcolor'] || undefined,
52214                     tabIndex:-1,
52215                     menu : new Roo.menu.ColorMenu({
52216                         focus: Roo.emptyFn,
52217                         value:'FFFFFF',
52218                         plain:true,
52219                         allowReselect: true,
52220                         selectHandler: function(cp, color){
52221                             if(Roo.isGecko){
52222                                 editorcore.execCmd('useCSS', false);
52223                                 editorcore.execCmd('hilitecolor', color);
52224                                 editorcore.execCmd('useCSS', true);
52225                                 editor.deferFocus();
52226                             }else{
52227                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
52228                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
52229                                 editor.deferFocus();
52230                             }
52231                         },
52232                         scope:editorcore,
52233                         clickEvent:'mousedown'
52234                     })
52235                 }
52236             );
52237         };
52238         // now add all the items...
52239         
52240
52241         if(!this.disable.alignments){
52242             tb.add(
52243                 '-',
52244                 btn('justifyleft'),
52245                 btn('justifycenter'),
52246                 btn('justifyright')
52247             );
52248         };
52249
52250         //if(!Roo.isSafari){
52251             if(!this.disable.links){
52252                 tb.add(
52253                     '-',
52254                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
52255                 );
52256             };
52257
52258             if(!this.disable.lists){
52259                 tb.add(
52260                     '-',
52261                     btn('insertorderedlist'),
52262                     btn('insertunorderedlist')
52263                 );
52264             }
52265             if(!this.disable.sourceEdit){
52266                 tb.add(
52267                     '-',
52268                     btn('sourceedit', true, function(btn){
52269                         this.toggleSourceEdit(btn.pressed);
52270                     })
52271                 );
52272             }
52273         //}
52274         
52275         var smenu = { };
52276         // special menu.. - needs to be tidied up..
52277         if (!this.disable.special) {
52278             smenu = {
52279                 text: "&#169;",
52280                 cls: 'x-edit-none',
52281                 
52282                 menu : {
52283                     items : []
52284                 }
52285             };
52286             for (var i =0; i < this.specialChars.length; i++) {
52287                 smenu.menu.items.push({
52288                     
52289                     html: this.specialChars[i],
52290                     handler: function(a,b) {
52291                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52292                         //editor.insertAtCursor(a.html);
52293                         
52294                     },
52295                     tabIndex:-1
52296                 });
52297             }
52298             
52299             
52300             tb.add(smenu);
52301             
52302             
52303         }
52304         
52305         var cmenu = { };
52306         if (!this.disable.cleanStyles) {
52307             cmenu = {
52308                 cls: 'x-btn-icon x-btn-clear',
52309                 
52310                 menu : {
52311                     items : []
52312                 }
52313             };
52314             for (var i =0; i < this.cleanStyles.length; i++) {
52315                 cmenu.menu.items.push({
52316                     actiontype : this.cleanStyles[i],
52317                     html: 'Remove ' + this.cleanStyles[i],
52318                     handler: function(a,b) {
52319 //                        Roo.log(a);
52320 //                        Roo.log(b);
52321                         var c = Roo.get(editorcore.doc.body);
52322                         c.select('[style]').each(function(s) {
52323                             s.dom.style.removeProperty(a.actiontype);
52324                         });
52325                         editorcore.syncValue();
52326                     },
52327                     tabIndex:-1
52328                 });
52329             }
52330             cmenu.menu.items.push({
52331                 actiontype : 'tablewidths',
52332                 html: 'Remove Table Widths',
52333                 handler: function(a,b) {
52334                     editorcore.cleanTableWidths();
52335                     editorcore.syncValue();
52336                 },
52337                 tabIndex:-1
52338             });
52339             cmenu.menu.items.push({
52340                 actiontype : 'word',
52341                 html: 'Remove MS Word Formating',
52342                 handler: function(a,b) {
52343                     editorcore.cleanWord();
52344                     editorcore.syncValue();
52345                 },
52346                 tabIndex:-1
52347             });
52348             
52349             cmenu.menu.items.push({
52350                 actiontype : 'all',
52351                 html: 'Remove All Styles',
52352                 handler: function(a,b) {
52353                     
52354                     var c = Roo.get(editorcore.doc.body);
52355                     c.select('[style]').each(function(s) {
52356                         s.dom.removeAttribute('style');
52357                     });
52358                     editorcore.syncValue();
52359                 },
52360                 tabIndex:-1
52361             });
52362             
52363             cmenu.menu.items.push({
52364                 actiontype : 'all',
52365                 html: 'Remove All CSS Classes',
52366                 handler: function(a,b) {
52367                     
52368                     var c = Roo.get(editorcore.doc.body);
52369                     c.select('[class]').each(function(s) {
52370                         s.dom.removeAttribute('class');
52371                     });
52372                     editorcore.cleanWord();
52373                     editorcore.syncValue();
52374                 },
52375                 tabIndex:-1
52376             });
52377             
52378              cmenu.menu.items.push({
52379                 actiontype : 'tidy',
52380                 html: 'Tidy HTML Source',
52381                 handler: function(a,b) {
52382                     new Roo.htmleditor.Tidy(editorcore.doc.body);
52383                     editorcore.syncValue();
52384                 },
52385                 tabIndex:-1
52386             });
52387             
52388             
52389             tb.add(cmenu);
52390         }
52391          
52392         if (!this.disable.specialElements) {
52393             var semenu = {
52394                 text: "Other;",
52395                 cls: 'x-edit-none',
52396                 menu : {
52397                     items : []
52398                 }
52399             };
52400             for (var i =0; i < this.specialElements.length; i++) {
52401                 semenu.menu.items.push(
52402                     Roo.apply({ 
52403                         handler: function(a,b) {
52404                             editor.insertAtCursor(this.ihtml);
52405                         }
52406                     }, this.specialElements[i])
52407                 );
52408                     
52409             }
52410             
52411             tb.add(semenu);
52412             
52413             
52414         }
52415          
52416         
52417         if (this.btns) {
52418             for(var i =0; i< this.btns.length;i++) {
52419                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52420                 b.cls =  'x-edit-none';
52421                 
52422                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52423                     b.cls += ' x-init-enable';
52424                 }
52425                 
52426                 b.scope = editorcore;
52427                 tb.add(b);
52428             }
52429         
52430         }
52431         
52432         
52433         
52434         // disable everything...
52435         
52436         this.tb.items.each(function(item){
52437             
52438            if(
52439                 item.id != editorcore.frameId+ '-sourceedit' && 
52440                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52441             ){
52442                 
52443                 item.disable();
52444             }
52445         });
52446         this.rendered = true;
52447         
52448         // the all the btns;
52449         editor.on('editorevent', this.updateToolbar, this);
52450         // other toolbars need to implement this..
52451         //editor.on('editmodechange', this.updateToolbar, this);
52452     },
52453     
52454     
52455     relayBtnCmd : function(btn) {
52456         this.editorcore.relayCmd(btn.cmd);
52457     },
52458     // private used internally
52459     createLink : function(){
52460         //Roo.log("create link?");
52461         var ec = this.editorcore;
52462         var ar = ec.getAllAncestors();
52463         var n = false;
52464         for(var i = 0;i< ar.length;i++) {
52465             if (ar[i] && ar[i].nodeName == 'A') {
52466                 n = ar[i];
52467                 break;
52468             }
52469         }
52470         
52471         (function() {
52472             
52473             Roo.MessageBox.show({
52474                 title : "Add / Edit Link URL",
52475                 msg : "Enter the url for the link",
52476                 buttons: Roo.MessageBox.OKCANCEL,
52477                 fn: function(btn, url){
52478                     if (btn != 'ok') {
52479                         return;
52480                     }
52481                     if(url && url != 'http:/'+'/'){
52482                         if (n) {
52483                             n.setAttribute('href', url);
52484                         } else {
52485                             ec.relayCmd('createlink', url);
52486                         }
52487                     }
52488                 },
52489                 minWidth:250,
52490                 prompt:true,
52491                 //multiline: multiline,
52492                 modal : true,
52493                 value :  n  ? n.getAttribute('href') : '' 
52494             });
52495             
52496              
52497         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52498         
52499     },
52500
52501     
52502     /**
52503      * Protected method that will not generally be called directly. It triggers
52504      * a toolbar update by reading the markup state of the current selection in the editor.
52505      */
52506     updateToolbar: function(){
52507
52508         if(!this.editorcore.activated){
52509             this.editor.onFirstFocus();
52510             return;
52511         }
52512
52513         var btns = this.tb.items.map, 
52514             doc = this.editorcore.doc,
52515             frameId = this.editorcore.frameId;
52516
52517         if(!this.disable.font && !Roo.isSafari){
52518             /*
52519             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52520             if(name != this.fontSelect.dom.value){
52521                 this.fontSelect.dom.value = name;
52522             }
52523             */
52524         }
52525         if(!this.disable.format){
52526             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52527             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52528             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52529             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52530         }
52531         if(!this.disable.alignments){
52532             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52533             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52534             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52535         }
52536         if(!Roo.isSafari && !this.disable.lists){
52537             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52538             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52539         }
52540         
52541         var ans = this.editorcore.getAllAncestors();
52542         if (this.formatCombo) {
52543             
52544             
52545             var store = this.formatCombo.store;
52546             this.formatCombo.setValue("");
52547             for (var i =0; i < ans.length;i++) {
52548                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52549                     // select it..
52550                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52551                     break;
52552                 }
52553             }
52554         }
52555         
52556         
52557         
52558         // hides menus... - so this cant be on a menu...
52559         Roo.menu.MenuMgr.hideAll();
52560
52561         //this.editorsyncValue();
52562     },
52563    
52564     
52565     createFontOptions : function(){
52566         var buf = [], fs = this.fontFamilies, ff, lc;
52567         
52568         
52569         
52570         for(var i = 0, len = fs.length; i< len; i++){
52571             ff = fs[i];
52572             lc = ff.toLowerCase();
52573             buf.push(
52574                 '<option value="',lc,'" style="font-family:',ff,';"',
52575                     (this.defaultFont == lc ? ' selected="true">' : '>'),
52576                     ff,
52577                 '</option>'
52578             );
52579         }
52580         return buf.join('');
52581     },
52582     
52583     toggleSourceEdit : function(sourceEditMode){
52584         
52585         Roo.log("toolbar toogle");
52586         if(sourceEditMode === undefined){
52587             sourceEditMode = !this.sourceEditMode;
52588         }
52589         this.sourceEditMode = sourceEditMode === true;
52590         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52591         // just toggle the button?
52592         if(btn.pressed !== this.sourceEditMode){
52593             btn.toggle(this.sourceEditMode);
52594             return;
52595         }
52596         
52597         if(sourceEditMode){
52598             Roo.log("disabling buttons");
52599             this.tb.items.each(function(item){
52600                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52601                     item.disable();
52602                 }
52603             });
52604           
52605         }else{
52606             Roo.log("enabling buttons");
52607             if(this.editorcore.initialized){
52608                 this.tb.items.each(function(item){
52609                     item.enable();
52610                 });
52611                 // initialize 'blocks'
52612                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52613                     Roo.htmleditor.Block.factory(e).updateElement(e);
52614                 },this);
52615             
52616             }
52617             
52618         }
52619         Roo.log("calling toggole on editor");
52620         // tell the editor that it's been pressed..
52621         this.editor.toggleSourceEdit(sourceEditMode);
52622        
52623     },
52624      /**
52625      * Object collection of toolbar tooltips for the buttons in the editor. The key
52626      * is the command id associated with that button and the value is a valid QuickTips object.
52627      * For example:
52628 <pre><code>
52629 {
52630     bold : {
52631         title: 'Bold (Ctrl+B)',
52632         text: 'Make the selected text bold.',
52633         cls: 'x-html-editor-tip'
52634     },
52635     italic : {
52636         title: 'Italic (Ctrl+I)',
52637         text: 'Make the selected text italic.',
52638         cls: 'x-html-editor-tip'
52639     },
52640     ...
52641 </code></pre>
52642     * @type Object
52643      */
52644     buttonTips : {
52645         bold : {
52646             title: 'Bold (Ctrl+B)',
52647             text: 'Make the selected text bold.',
52648             cls: 'x-html-editor-tip'
52649         },
52650         italic : {
52651             title: 'Italic (Ctrl+I)',
52652             text: 'Make the selected text italic.',
52653             cls: 'x-html-editor-tip'
52654         },
52655         underline : {
52656             title: 'Underline (Ctrl+U)',
52657             text: 'Underline the selected text.',
52658             cls: 'x-html-editor-tip'
52659         },
52660         strikethrough : {
52661             title: 'Strikethrough',
52662             text: 'Strikethrough the selected text.',
52663             cls: 'x-html-editor-tip'
52664         },
52665         increasefontsize : {
52666             title: 'Grow Text',
52667             text: 'Increase the font size.',
52668             cls: 'x-html-editor-tip'
52669         },
52670         decreasefontsize : {
52671             title: 'Shrink Text',
52672             text: 'Decrease the font size.',
52673             cls: 'x-html-editor-tip'
52674         },
52675         backcolor : {
52676             title: 'Text Highlight Color',
52677             text: 'Change the background color of the selected text.',
52678             cls: 'x-html-editor-tip'
52679         },
52680         forecolor : {
52681             title: 'Font Color',
52682             text: 'Change the color of the selected text.',
52683             cls: 'x-html-editor-tip'
52684         },
52685         justifyleft : {
52686             title: 'Align Text Left',
52687             text: 'Align text to the left.',
52688             cls: 'x-html-editor-tip'
52689         },
52690         justifycenter : {
52691             title: 'Center Text',
52692             text: 'Center text in the editor.',
52693             cls: 'x-html-editor-tip'
52694         },
52695         justifyright : {
52696             title: 'Align Text Right',
52697             text: 'Align text to the right.',
52698             cls: 'x-html-editor-tip'
52699         },
52700         insertunorderedlist : {
52701             title: 'Bullet List',
52702             text: 'Start a bulleted list.',
52703             cls: 'x-html-editor-tip'
52704         },
52705         insertorderedlist : {
52706             title: 'Numbered List',
52707             text: 'Start a numbered list.',
52708             cls: 'x-html-editor-tip'
52709         },
52710         createlink : {
52711             title: 'Hyperlink',
52712             text: 'Make the selected text a hyperlink.',
52713             cls: 'x-html-editor-tip'
52714         },
52715         sourceedit : {
52716             title: 'Source Edit',
52717             text: 'Switch to source editing mode.',
52718             cls: 'x-html-editor-tip'
52719         }
52720     },
52721     // private
52722     onDestroy : function(){
52723         if(this.rendered){
52724             
52725             this.tb.items.each(function(item){
52726                 if(item.menu){
52727                     item.menu.removeAll();
52728                     if(item.menu.el){
52729                         item.menu.el.destroy();
52730                     }
52731                 }
52732                 item.destroy();
52733             });
52734              
52735         }
52736     },
52737     onFirstFocus: function() {
52738         this.tb.items.each(function(item){
52739            item.enable();
52740         });
52741     }
52742 };
52743
52744
52745
52746
52747 // <script type="text/javascript">
52748 /*
52749  * Based on
52750  * Ext JS Library 1.1.1
52751  * Copyright(c) 2006-2007, Ext JS, LLC.
52752  *  
52753  
52754  */
52755
52756  
52757 /**
52758  * @class Roo.form.HtmlEditor.ToolbarContext
52759  * Context Toolbar
52760  * 
52761  * Usage:
52762  *
52763  new Roo.form.HtmlEditor({
52764     ....
52765     toolbars : [
52766         { xtype: 'ToolbarStandard', styles : {} }
52767         { xtype: 'ToolbarContext', disable : {} }
52768     ]
52769 })
52770
52771      
52772  * 
52773  * @config : {Object} disable List of elements to disable.. (not done yet.)
52774  * @config : {Object} styles  Map of styles available.
52775  * 
52776  */
52777
52778 Roo.form.HtmlEditor.ToolbarContext = function(config)
52779 {
52780     
52781     Roo.apply(this, config);
52782     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52783     // dont call parent... till later.
52784     this.styles = this.styles || {};
52785 }
52786
52787  
52788
52789 Roo.form.HtmlEditor.ToolbarContext.types = {
52790     'IMG' : [
52791         {
52792             name : 'width',
52793             title: "Width",
52794             width: 40
52795         },
52796         {
52797             name : 'height',
52798             title: "Height",
52799             width: 40
52800         },
52801         {
52802             name : 'align',
52803             title: "Align",
52804             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52805             width : 80
52806             
52807         },
52808         {
52809             name : 'border',
52810             title: "Border",
52811             width: 40
52812         },
52813         {
52814             name : 'alt',
52815             title: "Alt",
52816             width: 120
52817         },
52818         {
52819             name : 'src',
52820             title: "Src",
52821             width: 220
52822         }
52823         
52824     ],
52825     
52826     'FIGURE' : [
52827         {
52828             name : 'align',
52829             title: "Align",
52830             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52831             width : 80  
52832         }
52833     ],
52834     'A' : [
52835         {
52836             name : 'name',
52837             title: "Name",
52838             width: 50
52839         },
52840         {
52841             name : 'target',
52842             title: "Target",
52843             width: 120
52844         },
52845         {
52846             name : 'href',
52847             title: "Href",
52848             width: 220
52849         } // border?
52850         
52851     ],
52852     
52853     'INPUT' : [
52854         {
52855             name : 'name',
52856             title: "name",
52857             width: 120
52858         },
52859         {
52860             name : 'value',
52861             title: "Value",
52862             width: 120
52863         },
52864         {
52865             name : 'width',
52866             title: "Width",
52867             width: 40
52868         }
52869     ],
52870     'LABEL' : [
52871          {
52872             name : 'for',
52873             title: "For",
52874             width: 120
52875         }
52876     ],
52877     'TEXTAREA' : [
52878         {
52879             name : 'name',
52880             title: "name",
52881             width: 120
52882         },
52883         {
52884             name : 'rows',
52885             title: "Rows",
52886             width: 20
52887         },
52888         {
52889             name : 'cols',
52890             title: "Cols",
52891             width: 20
52892         }
52893     ],
52894     'SELECT' : [
52895         {
52896             name : 'name',
52897             title: "name",
52898             width: 120
52899         },
52900         {
52901             name : 'selectoptions',
52902             title: "Options",
52903             width: 200
52904         }
52905     ],
52906     
52907     // should we really allow this??
52908     // should this just be 
52909     'BODY' : [
52910         
52911         {
52912             name : 'title',
52913             title: "Title",
52914             width: 200,
52915             disabled : true
52916         }
52917     ],
52918  
52919     '*' : [
52920         // empty.
52921     ]
52922
52923 };
52924
52925 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
52926 Roo.form.HtmlEditor.ToolbarContext.stores = false;
52927
52928 Roo.form.HtmlEditor.ToolbarContext.options = {
52929         'font-family'  : [ 
52930                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
52931                 [ 'Courier New', 'Courier New'],
52932                 [ 'Tahoma', 'Tahoma'],
52933                 [ 'Times New Roman,serif', 'Times'],
52934                 [ 'Verdana','Verdana' ]
52935         ]
52936 };
52937
52938 // fixme - these need to be configurable..
52939  
52940
52941 //Roo.form.HtmlEditor.ToolbarContext.types
52942
52943
52944 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
52945     
52946     tb: false,
52947     
52948     rendered: false,
52949     
52950     editor : false,
52951     editorcore : false,
52952     /**
52953      * @cfg {Object} disable  List of toolbar elements to disable
52954          
52955      */
52956     disable : false,
52957     /**
52958      * @cfg {Object} styles List of styles 
52959      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
52960      *
52961      * These must be defined in the page, so they get rendered correctly..
52962      * .headline { }
52963      * TD.underline { }
52964      * 
52965      */
52966     styles : false,
52967     
52968     options: false,
52969     
52970     toolbars : false,
52971     
52972     init : function(editor)
52973     {
52974         this.editor = editor;
52975         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52976         var editorcore = this.editorcore;
52977         
52978         var fid = editorcore.frameId;
52979         var etb = this;
52980         function btn(id, toggle, handler){
52981             var xid = fid + '-'+ id ;
52982             return {
52983                 id : xid,
52984                 cmd : id,
52985                 cls : 'x-btn-icon x-edit-'+id,
52986                 enableToggle:toggle !== false,
52987                 scope: editorcore, // was editor...
52988                 handler:handler||editorcore.relayBtnCmd,
52989                 clickEvent:'mousedown',
52990                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52991                 tabIndex:-1
52992             };
52993         }
52994         // create a new element.
52995         var wdiv = editor.wrap.createChild({
52996                 tag: 'div'
52997             }, editor.wrap.dom.firstChild.nextSibling, true);
52998         
52999         // can we do this more than once??
53000         
53001          // stop form submits
53002       
53003  
53004         // disable everything...
53005         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53006         this.toolbars = {};
53007         // block toolbars are built in updateToolbar when needed.
53008         for (var i in  ty) {
53009             
53010             this.toolbars[i] = this.buildToolbar(ty[i],i);
53011         }
53012         this.tb = this.toolbars.BODY;
53013         this.tb.el.show();
53014         this.buildFooter();
53015         this.footer.show();
53016         editor.on('hide', function( ) { this.footer.hide() }, this);
53017         editor.on('show', function( ) { this.footer.show() }, this);
53018         
53019          
53020         this.rendered = true;
53021         
53022         // the all the btns;
53023         editor.on('editorevent', this.updateToolbar, this);
53024         // other toolbars need to implement this..
53025         //editor.on('editmodechange', this.updateToolbar, this);
53026     },
53027     
53028     
53029     
53030     /**
53031      * Protected method that will not generally be called directly. It triggers
53032      * a toolbar update by reading the markup state of the current selection in the editor.
53033      *
53034      * Note you can force an update by calling on('editorevent', scope, false)
53035      */
53036     updateToolbar: function(editor ,ev, sel)
53037     {
53038         
53039         if (ev) {
53040             ev.stopEvent(); // se if we can stop this looping with mutiple events.
53041         }
53042         
53043         //Roo.log(ev);
53044         // capture mouse up - this is handy for selecting images..
53045         // perhaps should go somewhere else...
53046         if(!this.editorcore.activated){
53047              this.editor.onFirstFocus();
53048             return;
53049         }
53050         //Roo.log(ev ? ev.target : 'NOTARGET');
53051         
53052         
53053         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53054         // selectNode - might want to handle IE?
53055         
53056         
53057         
53058         if (ev &&
53059             (ev.type == 'mouseup' || ev.type == 'click' ) &&
53060             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53061             // they have click on an image...
53062             // let's see if we can change the selection...
53063             sel = ev.target;
53064             
53065             // this triggers looping?
53066             //this.editorcore.selectNode(sel);
53067              
53068         }
53069         
53070         // this forces an id..
53071         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53072              e.classList.remove('roo-ed-selection');
53073         });
53074         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53075         //Roo.get(node).addClass('roo-ed-selection');
53076       
53077         //var updateFooter = sel ? false : true; 
53078         
53079         
53080         var ans = this.editorcore.getAllAncestors();
53081         
53082         // pick
53083         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53084         
53085         if (!sel) { 
53086             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
53087             sel = sel ? sel : this.editorcore.doc.body;
53088             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53089             
53090         }
53091         
53092         var tn = sel.tagName.toUpperCase();
53093         var lastSel = this.tb.selectedNode;
53094         this.tb.selectedNode = sel;
53095         var left_label = tn;
53096         
53097         // ok see if we are editing a block?
53098         
53099         var db = false;
53100         // you are not actually selecting the block.
53101         if (sel && sel.hasAttribute('data-block')) {
53102             db = sel;
53103         } else if (sel && sel.closest('[data-block]')) {
53104             
53105             db = sel.closest('[data-block]');
53106             //var cepar = sel.closest('[contenteditable=true]');
53107             //if (db && cepar && cepar.tagName != 'BODY') {
53108             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53109             //}   
53110         }
53111         
53112         
53113         var block = false;
53114         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53115         if (db && this.editorcore.enableBlocks) {
53116             block = Roo.htmleditor.Block.factory(db);
53117             
53118             
53119             if (block) {
53120                  db.className = (
53121                         db.classList.length > 0  ? db.className + ' ' : ''
53122                     )  + 'roo-ed-selection';
53123                  
53124                  // since we removed it earlier... its not there..
53125                 tn = 'BLOCK.' + db.getAttribute('data-block');
53126                 
53127                 //this.editorcore.selectNode(db);
53128                 if (typeof(this.toolbars[tn]) == 'undefined') {
53129                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
53130                 }
53131                 this.toolbars[tn].selectedNode = db;
53132                 left_label = block.friendly_name;
53133                 ans = this.editorcore.getAllAncestors();
53134             }
53135             
53136                 
53137             
53138         }
53139         
53140         
53141         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53142             return; // no change?
53143         }
53144         
53145         
53146           
53147         this.tb.el.hide();
53148         ///console.log("show: " + tn);
53149         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53150         
53151         this.tb.el.show();
53152         // update name
53153         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
53154         
53155         
53156         // update attributes
53157         if (block && this.tb.fields) {
53158              
53159             this.tb.fields.each(function(e) {
53160                 e.setValue(block[e.name]);
53161             });
53162             
53163             
53164         } else  if (this.tb.fields && this.tb.selectedNode) {
53165             this.tb.fields.each( function(e) {
53166                 if (e.stylename) {
53167                     e.setValue(this.tb.selectedNode.style[e.stylename]);
53168                     return;
53169                 } 
53170                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53171             }, this);
53172             this.updateToolbarStyles(this.tb.selectedNode);  
53173         }
53174         
53175         
53176        
53177         Roo.menu.MenuMgr.hideAll();
53178
53179         
53180         
53181     
53182         // update the footer
53183         //
53184         this.updateFooter(ans);
53185              
53186     },
53187     
53188     updateToolbarStyles : function(sel)
53189     {
53190         var hasStyles = false;
53191         for(var i in this.styles) {
53192             hasStyles = true;
53193             break;
53194         }
53195         
53196         // update styles
53197         if (hasStyles && this.tb.hasStyles) { 
53198             var st = this.tb.fields.item(0);
53199             
53200             st.store.removeAll();
53201             var cn = sel.className.split(/\s+/);
53202             
53203             var avs = [];
53204             if (this.styles['*']) {
53205                 
53206                 Roo.each(this.styles['*'], function(v) {
53207                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53208                 });
53209             }
53210             if (this.styles[tn]) { 
53211                 Roo.each(this.styles[tn], function(v) {
53212                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53213                 });
53214             }
53215             
53216             st.store.loadData(avs);
53217             st.collapse();
53218             st.setValue(cn);
53219         }
53220     },
53221     
53222      
53223     updateFooter : function(ans)
53224     {
53225         var html = '';
53226         if (ans === false) {
53227             this.footDisp.dom.innerHTML = '';
53228             return;
53229         }
53230         
53231         this.footerEls = ans.reverse();
53232         Roo.each(this.footerEls, function(a,i) {
53233             if (!a) { return; }
53234             html += html.length ? ' &gt; '  :  '';
53235             
53236             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53237             
53238         });
53239        
53240         // 
53241         var sz = this.footDisp.up('td').getSize();
53242         this.footDisp.dom.style.width = (sz.width -10) + 'px';
53243         this.footDisp.dom.style.marginLeft = '5px';
53244         
53245         this.footDisp.dom.style.overflow = 'hidden';
53246         
53247         this.footDisp.dom.innerHTML = html;
53248             
53249         
53250     },
53251    
53252        
53253     // private
53254     onDestroy : function(){
53255         if(this.rendered){
53256             
53257             this.tb.items.each(function(item){
53258                 if(item.menu){
53259                     item.menu.removeAll();
53260                     if(item.menu.el){
53261                         item.menu.el.destroy();
53262                     }
53263                 }
53264                 item.destroy();
53265             });
53266              
53267         }
53268     },
53269     onFirstFocus: function() {
53270         // need to do this for all the toolbars..
53271         this.tb.items.each(function(item){
53272            item.enable();
53273         });
53274     },
53275     buildToolbar: function(tlist, nm, friendly_name, block)
53276     {
53277         var editor = this.editor;
53278         var editorcore = this.editorcore;
53279          // create a new element.
53280         var wdiv = editor.wrap.createChild({
53281                 tag: 'div'
53282             }, editor.wrap.dom.firstChild.nextSibling, true);
53283         
53284        
53285         var tb = new Roo.Toolbar(wdiv);
53286         ///this.tb = tb; // << this sets the active toolbar..
53287         if (tlist === false && block) {
53288             tlist = block.contextMenu(this);
53289         }
53290         
53291         tb.hasStyles = false;
53292         tb.name = nm;
53293         
53294         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
53295         
53296         var styles = Array.from(this.styles);
53297         
53298         
53299         // styles...
53300         if (styles && styles.length) {
53301             tb.hasStyles = true;
53302             // this needs a multi-select checkbox...
53303             tb.addField( new Roo.form.ComboBox({
53304                 store: new Roo.data.SimpleStore({
53305                     id : 'val',
53306                     fields: ['val', 'selected'],
53307                     data : [] 
53308                 }),
53309                 name : '-roo-edit-className',
53310                 attrname : 'className',
53311                 displayField: 'val',
53312                 typeAhead: false,
53313                 mode: 'local',
53314                 editable : false,
53315                 triggerAction: 'all',
53316                 emptyText:'Select Style',
53317                 selectOnFocus:true,
53318                 width: 130,
53319                 listeners : {
53320                     'select': function(c, r, i) {
53321                         // initial support only for on class per el..
53322                         tb.selectedNode.className =  r ? r.get('val') : '';
53323                         editorcore.syncValue();
53324                     }
53325                 }
53326     
53327             }));
53328         }
53329         
53330         var tbc = Roo.form.HtmlEditor.ToolbarContext;
53331         
53332         
53333         for (var i = 0; i < tlist.length; i++) {
53334             
53335             // newer versions will use xtype cfg to create menus.
53336             if (typeof(tlist[i].xtype) != 'undefined') {
53337                 
53338                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53339                 
53340                 
53341                 continue;
53342             }
53343             
53344             var item = tlist[i];
53345             tb.add(item.title + ":&nbsp;");
53346             
53347             
53348             //optname == used so you can configure the options available..
53349             var opts = item.opts ? item.opts : false;
53350             if (item.optname) { // use the b
53351                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53352            
53353             }
53354             
53355             if (opts) {
53356                 // opts == pulldown..
53357                 tb.addField( new Roo.form.ComboBox({
53358                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53359                         id : 'val',
53360                         fields: ['val', 'display'],
53361                         data : opts  
53362                     }),
53363                     name : '-roo-edit-' + tlist[i].name,
53364                     
53365                     attrname : tlist[i].name,
53366                     stylename : item.style ? item.style : false,
53367                     
53368                     displayField: item.displayField ? item.displayField : 'val',
53369                     valueField :  'val',
53370                     typeAhead: false,
53371                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
53372                     editable : false,
53373                     triggerAction: 'all',
53374                     emptyText:'Select',
53375                     selectOnFocus:true,
53376                     width: item.width ? item.width  : 130,
53377                     listeners : {
53378                         'select': function(c, r, i) {
53379                              
53380                             
53381                             if (c.stylename) {
53382                                 tb.selectedNode.style[c.stylename] =  r.get('val');
53383                                 editorcore.syncValue();
53384                                 return;
53385                             }
53386                             if (r === false) {
53387                                 tb.selectedNode.removeAttribute(c.attrname);
53388                                 editorcore.syncValue();
53389                                 return;
53390                             }
53391                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53392                             editorcore.syncValue();
53393                         }
53394                     }
53395
53396                 }));
53397                 continue;
53398                     
53399                  
53400                 /*
53401                 tb.addField( new Roo.form.TextField({
53402                     name: i,
53403                     width: 100,
53404                     //allowBlank:false,
53405                     value: ''
53406                 }));
53407                 continue;
53408                 */
53409             }
53410             tb.addField( new Roo.form.TextField({
53411                 name: '-roo-edit-' + tlist[i].name,
53412                 attrname : tlist[i].name,
53413                 
53414                 width: item.width,
53415                 //allowBlank:true,
53416                 value: '',
53417                 listeners: {
53418                     'change' : function(f, nv, ov) {
53419                         
53420                          
53421                         tb.selectedNode.setAttribute(f.attrname, nv);
53422                         editorcore.syncValue();
53423                     }
53424                 }
53425             }));
53426              
53427         }
53428         
53429         var _this = this;
53430         var show_delete = !block || block.deleteTitle !== false;
53431         if(nm == 'BODY'){
53432             show_delete = false;
53433             tb.addSeparator();
53434         
53435             tb.addButton( {
53436                 text: 'Stylesheets',
53437
53438                 listeners : {
53439                     click : function ()
53440                     {
53441                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
53442                     }
53443                 }
53444             });
53445         }
53446         
53447         tb.addFill();
53448         if (show_delete) {
53449             tb.addButton({
53450                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53451         
53452                 listeners : {
53453                     click : function ()
53454                     {
53455                         var sn = tb.selectedNode;
53456                         if (block) {
53457                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53458                             
53459                         }
53460                         if (!sn) {
53461                             return;
53462                         }
53463                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53464                         if (sn.hasAttribute('data-block')) {
53465                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
53466                             sn.parentNode.removeChild(sn);
53467                             
53468                         } else if (sn && sn.tagName != 'BODY') {
53469                             // remove and keep parents.
53470                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53471                             a.replaceTag(sn);
53472                         }
53473                         
53474                         
53475                         var range = editorcore.createRange();
53476             
53477                         range.setStart(stn,0);
53478                         range.setEnd(stn,0); 
53479                         var selection = editorcore.getSelection();
53480                         selection.removeAllRanges();
53481                         selection.addRange(range);
53482                         
53483                         
53484                         //_this.updateToolbar(null, null, pn);
53485                         _this.updateToolbar(null, null, null);
53486                         _this.updateFooter(false);
53487                         
53488                     }
53489                 }
53490                 
53491                         
53492                     
53493                 
53494             });
53495         }    
53496         
53497         tb.el.on('click', function(e){
53498             e.preventDefault(); // what does this do?
53499         });
53500         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53501         tb.el.hide();
53502         
53503         // dont need to disable them... as they will get hidden
53504         return tb;
53505          
53506         
53507     },
53508     buildFooter : function()
53509     {
53510         
53511         var fel = this.editor.wrap.createChild();
53512         this.footer = new Roo.Toolbar(fel);
53513         // toolbar has scrolly on left / right?
53514         var footDisp= new Roo.Toolbar.Fill();
53515         var _t = this;
53516         this.footer.add(
53517             {
53518                 text : '&lt;',
53519                 xtype: 'Button',
53520                 handler : function() {
53521                     _t.footDisp.scrollTo('left',0,true)
53522                 }
53523             }
53524         );
53525         this.footer.add( footDisp );
53526         this.footer.add( 
53527             {
53528                 text : '&gt;',
53529                 xtype: 'Button',
53530                 handler : function() {
53531                     // no animation..
53532                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53533                 }
53534             }
53535         );
53536         var fel = Roo.get(footDisp.el);
53537         fel.addClass('x-editor-context');
53538         this.footDispWrap = fel; 
53539         this.footDispWrap.overflow  = 'hidden';
53540         
53541         this.footDisp = fel.createChild();
53542         this.footDispWrap.on('click', this.onContextClick, this)
53543         
53544         
53545     },
53546     // when the footer contect changes
53547     onContextClick : function (ev,dom)
53548     {
53549         ev.preventDefault();
53550         var  cn = dom.className;
53551         //Roo.log(cn);
53552         if (!cn.match(/x-ed-loc-/)) {
53553             return;
53554         }
53555         var n = cn.split('-').pop();
53556         var ans = this.footerEls;
53557         var sel = ans[n];
53558         
53559         this.editorcore.selectNode(sel);
53560         
53561         
53562         this.updateToolbar(null, null, sel);
53563         
53564         
53565     }
53566     
53567     
53568     
53569     
53570     
53571 });
53572
53573
53574
53575
53576
53577 /*
53578  * Based on:
53579  * Ext JS Library 1.1.1
53580  * Copyright(c) 2006-2007, Ext JS, LLC.
53581  *
53582  * Originally Released Under LGPL - original licence link has changed is not relivant.
53583  *
53584  * Fork - LGPL
53585  * <script type="text/javascript">
53586  */
53587  
53588 /**
53589  * @class Roo.form.BasicForm
53590  * @extends Roo.util.Observable
53591  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53592  * @constructor
53593  * @param {String/HTMLElement/Roo.Element} el The form element or its id
53594  * @param {Object} config Configuration options
53595  */
53596 Roo.form.BasicForm = function(el, config){
53597     this.allItems = [];
53598     this.childForms = [];
53599     Roo.apply(this, config);
53600     /*
53601      * The Roo.form.Field items in this form.
53602      * @type MixedCollection
53603      */
53604      
53605      
53606     this.items = new Roo.util.MixedCollection(false, function(o){
53607         return o.id || (o.id = Roo.id());
53608     });
53609     this.addEvents({
53610         /**
53611          * @event beforeaction
53612          * Fires before any action is performed. Return false to cancel the action.
53613          * @param {Form} this
53614          * @param {Action} action The action to be performed
53615          */
53616         beforeaction: true,
53617         /**
53618          * @event actionfailed
53619          * Fires when an action fails.
53620          * @param {Form} this
53621          * @param {Action} action The action that failed
53622          */
53623         actionfailed : true,
53624         /**
53625          * @event actioncomplete
53626          * Fires when an action is completed.
53627          * @param {Form} this
53628          * @param {Action} action The action that completed
53629          */
53630         actioncomplete : true
53631     });
53632     if(el){
53633         this.initEl(el);
53634     }
53635     Roo.form.BasicForm.superclass.constructor.call(this);
53636     
53637     Roo.form.BasicForm.popover.apply();
53638 };
53639
53640 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53641     /**
53642      * @cfg {String} method
53643      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53644      */
53645     /**
53646      * @cfg {DataReader} reader
53647      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53648      * This is optional as there is built-in support for processing JSON.
53649      */
53650     /**
53651      * @cfg {DataReader} errorReader
53652      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53653      * This is completely optional as there is built-in support for processing JSON.
53654      */
53655     /**
53656      * @cfg {String} url
53657      * The URL to use for form actions if one isn't supplied in the action options.
53658      */
53659     /**
53660      * @cfg {Boolean} fileUpload
53661      * Set to true if this form is a file upload.
53662      */
53663      
53664     /**
53665      * @cfg {Object} baseParams
53666      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53667      */
53668      /**
53669      
53670     /**
53671      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53672      */
53673     timeout: 30,
53674
53675     // private
53676     activeAction : null,
53677
53678     /**
53679      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53680      * or setValues() data instead of when the form was first created.
53681      */
53682     trackResetOnLoad : false,
53683     
53684     
53685     /**
53686      * childForms - used for multi-tab forms
53687      * @type {Array}
53688      */
53689     childForms : false,
53690     
53691     /**
53692      * allItems - full list of fields.
53693      * @type {Array}
53694      */
53695     allItems : false,
53696     
53697     /**
53698      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53699      * element by passing it or its id or mask the form itself by passing in true.
53700      * @type Mixed
53701      */
53702     waitMsgTarget : false,
53703     
53704     /**
53705      * @type Boolean
53706      */
53707     disableMask : false,
53708     
53709     /**
53710      * @cfg {Boolean} errorMask (true|false) default false
53711      */
53712     errorMask : false,
53713     
53714     /**
53715      * @cfg {Number} maskOffset Default 100
53716      */
53717     maskOffset : 100,
53718
53719     // private
53720     initEl : function(el){
53721         this.el = Roo.get(el);
53722         this.id = this.el.id || Roo.id();
53723         this.el.on('submit', this.onSubmit, this);
53724         this.el.addClass('x-form');
53725     },
53726
53727     // private
53728     onSubmit : function(e){
53729         e.stopEvent();
53730     },
53731
53732     /**
53733      * Returns true if client-side validation on the form is successful.
53734      * @return Boolean
53735      */
53736     isValid : function(){
53737         var valid = true;
53738         var target = false;
53739         this.items.each(function(f){
53740             if(f.validate()){
53741                 return;
53742             }
53743             
53744             valid = false;
53745                 
53746             if(!target && f.el.isVisible(true)){
53747                 target = f;
53748             }
53749         });
53750         
53751         if(this.errorMask && !valid){
53752             Roo.form.BasicForm.popover.mask(this, target);
53753         }
53754         
53755         return valid;
53756     },
53757     /**
53758      * Returns array of invalid form fields.
53759      * @return Array
53760      */
53761     
53762     invalidFields : function()
53763     {
53764         var ret = [];
53765         this.items.each(function(f){
53766             if(f.validate()){
53767                 return;
53768             }
53769             ret.push(f);
53770             
53771         });
53772         
53773         return ret;
53774     },
53775     
53776     
53777     /**
53778      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
53779      * @return Boolean
53780      */
53781     isDirty : function(){
53782         var dirty = false;
53783         this.items.each(function(f){
53784            if(f.isDirty()){
53785                dirty = true;
53786                return false;
53787            }
53788         });
53789         return dirty;
53790     },
53791     
53792     /**
53793      * Returns true if any fields in this form have changed since their original load. (New version)
53794      * @return Boolean
53795      */
53796     
53797     hasChanged : function()
53798     {
53799         var dirty = false;
53800         this.items.each(function(f){
53801            if(f.hasChanged()){
53802                dirty = true;
53803                return false;
53804            }
53805         });
53806         return dirty;
53807         
53808     },
53809     /**
53810      * Resets all hasChanged to 'false' -
53811      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
53812      * So hasChanged storage is only to be used for this purpose
53813      * @return Boolean
53814      */
53815     resetHasChanged : function()
53816     {
53817         this.items.each(function(f){
53818            f.resetHasChanged();
53819         });
53820         
53821     },
53822     
53823     
53824     /**
53825      * Performs a predefined action (submit or load) or custom actions you define on this form.
53826      * @param {String} actionName The name of the action type
53827      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
53828      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
53829      * accept other config options):
53830      * <pre>
53831 Property          Type             Description
53832 ----------------  ---------------  ----------------------------------------------------------------------------------
53833 url               String           The url for the action (defaults to the form's url)
53834 method            String           The form method to use (defaults to the form's method, or POST if not defined)
53835 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
53836 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
53837                                    validate the form on the client (defaults to false)
53838      * </pre>
53839      * @return {BasicForm} this
53840      */
53841     doAction : function(action, options){
53842         if(typeof action == 'string'){
53843             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
53844         }
53845         if(this.fireEvent('beforeaction', this, action) !== false){
53846             this.beforeAction(action);
53847             action.run.defer(100, action);
53848         }
53849         return this;
53850     },
53851
53852     /**
53853      * Shortcut to do a submit action.
53854      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53855      * @return {BasicForm} this
53856      */
53857     submit : function(options){
53858         this.doAction('submit', options);
53859         return this;
53860     },
53861
53862     /**
53863      * Shortcut to do a load action.
53864      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53865      * @return {BasicForm} this
53866      */
53867     load : function(options){
53868         this.doAction('load', options);
53869         return this;
53870     },
53871
53872     /**
53873      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
53874      * @param {Record} record The record to edit
53875      * @return {BasicForm} this
53876      */
53877     updateRecord : function(record){
53878         record.beginEdit();
53879         var fs = record.fields;
53880         fs.each(function(f){
53881             var field = this.findField(f.name);
53882             if(field){
53883                 record.set(f.name, field.getValue());
53884             }
53885         }, this);
53886         record.endEdit();
53887         return this;
53888     },
53889
53890     /**
53891      * Loads an Roo.data.Record into this form.
53892      * @param {Record} record The record to load
53893      * @return {BasicForm} this
53894      */
53895     loadRecord : function(record){
53896         this.setValues(record.data);
53897         return this;
53898     },
53899
53900     // private
53901     beforeAction : function(action){
53902         var o = action.options;
53903         
53904         if(!this.disableMask) {
53905             if(this.waitMsgTarget === true){
53906                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
53907             }else if(this.waitMsgTarget){
53908                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
53909                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
53910             }else {
53911                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
53912             }
53913         }
53914         
53915          
53916     },
53917
53918     // private
53919     afterAction : function(action, success){
53920         this.activeAction = null;
53921         var o = action.options;
53922         
53923         if(!this.disableMask) {
53924             if(this.waitMsgTarget === true){
53925                 this.el.unmask();
53926             }else if(this.waitMsgTarget){
53927                 this.waitMsgTarget.unmask();
53928             }else{
53929                 Roo.MessageBox.updateProgress(1);
53930                 Roo.MessageBox.hide();
53931             }
53932         }
53933         
53934         if(success){
53935             if(o.reset){
53936                 this.reset();
53937             }
53938             Roo.callback(o.success, o.scope, [this, action]);
53939             this.fireEvent('actioncomplete', this, action);
53940             
53941         }else{
53942             
53943             // failure condition..
53944             // we have a scenario where updates need confirming.
53945             // eg. if a locking scenario exists..
53946             // we look for { errors : { needs_confirm : true }} in the response.
53947             if (
53948                 (typeof(action.result) != 'undefined')  &&
53949                 (typeof(action.result.errors) != 'undefined')  &&
53950                 (typeof(action.result.errors.needs_confirm) != 'undefined')
53951            ){
53952                 var _t = this;
53953                 Roo.MessageBox.confirm(
53954                     "Change requires confirmation",
53955                     action.result.errorMsg,
53956                     function(r) {
53957                         if (r != 'yes') {
53958                             return;
53959                         }
53960                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
53961                     }
53962                     
53963                 );
53964                 
53965                 
53966                 
53967                 return;
53968             }
53969             
53970             Roo.callback(o.failure, o.scope, [this, action]);
53971             // show an error message if no failed handler is set..
53972             if (!this.hasListener('actionfailed')) {
53973                 Roo.MessageBox.alert("Error",
53974                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
53975                         action.result.errorMsg :
53976                         "Saving Failed, please check your entries or try again"
53977                 );
53978             }
53979             
53980             this.fireEvent('actionfailed', this, action);
53981         }
53982         
53983     },
53984
53985     /**
53986      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
53987      * @param {String} id The value to search for
53988      * @return Field
53989      */
53990     findField : function(id){
53991         var field = this.items.get(id);
53992         if(!field){
53993             this.items.each(function(f){
53994                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
53995                     field = f;
53996                     return false;
53997                 }
53998             });
53999         }
54000         return field || null;
54001     },
54002
54003     /**
54004      * Add a secondary form to this one, 
54005      * Used to provide tabbed forms. One form is primary, with hidden values 
54006      * which mirror the elements from the other forms.
54007      * 
54008      * @param {Roo.form.Form} form to add.
54009      * 
54010      */
54011     addForm : function(form)
54012     {
54013        
54014         if (this.childForms.indexOf(form) > -1) {
54015             // already added..
54016             return;
54017         }
54018         this.childForms.push(form);
54019         var n = '';
54020         Roo.each(form.allItems, function (fe) {
54021             
54022             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54023             if (this.findField(n)) { // already added..
54024                 return;
54025             }
54026             var add = new Roo.form.Hidden({
54027                 name : n
54028             });
54029             add.render(this.el);
54030             
54031             this.add( add );
54032         }, this);
54033         
54034     },
54035     /**
54036      * Mark fields in this form invalid in bulk.
54037      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54038      * @return {BasicForm} this
54039      */
54040     markInvalid : function(errors){
54041         if(errors instanceof Array){
54042             for(var i = 0, len = errors.length; i < len; i++){
54043                 var fieldError = errors[i];
54044                 var f = this.findField(fieldError.id);
54045                 if(f){
54046                     f.markInvalid(fieldError.msg);
54047                 }
54048             }
54049         }else{
54050             var field, id;
54051             for(id in errors){
54052                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54053                     field.markInvalid(errors[id]);
54054                 }
54055             }
54056         }
54057         Roo.each(this.childForms || [], function (f) {
54058             f.markInvalid(errors);
54059         });
54060         
54061         return this;
54062     },
54063
54064     /**
54065      * Set values for fields in this form in bulk.
54066      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54067      * @return {BasicForm} this
54068      */
54069     setValues : function(values){
54070         if(values instanceof Array){ // array of objects
54071             for(var i = 0, len = values.length; i < len; i++){
54072                 var v = values[i];
54073                 var f = this.findField(v.id);
54074                 if(f){
54075                     f.setValue(v.value);
54076                     if(this.trackResetOnLoad){
54077                         f.originalValue = f.getValue();
54078                     }
54079                 }
54080             }
54081         }else{ // object hash
54082             var field, id;
54083             for(id in values){
54084                 if(typeof values[id] != 'function' && (field = this.findField(id))){
54085                     
54086                     if (field.setFromData && 
54087                         field.valueField && 
54088                         field.displayField &&
54089                         // combos' with local stores can 
54090                         // be queried via setValue()
54091                         // to set their value..
54092                         (field.store && !field.store.isLocal)
54093                         ) {
54094                         // it's a combo
54095                         var sd = { };
54096                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54097                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54098                         field.setFromData(sd);
54099                         
54100                     } else {
54101                         field.setValue(values[id]);
54102                     }
54103                     
54104                     
54105                     if(this.trackResetOnLoad){
54106                         field.originalValue = field.getValue();
54107                     }
54108                 }
54109             }
54110         }
54111         this.resetHasChanged();
54112         
54113         
54114         Roo.each(this.childForms || [], function (f) {
54115             f.setValues(values);
54116             f.resetHasChanged();
54117         });
54118                 
54119         return this;
54120     },
54121  
54122     /**
54123      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54124      * they are returned as an array.
54125      * @param {Boolean} asString
54126      * @return {Object}
54127      */
54128     getValues : function(asString)
54129     {
54130         if (this.childForms) {
54131             // copy values from the child forms
54132             Roo.each(this.childForms, function (f) {
54133                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54134             }, this);
54135         }
54136         
54137         // use formdata
54138         if (typeof(FormData) != 'undefined' && asString !== true) {
54139             // this relies on a 'recent' version of chrome apparently...
54140             try {
54141                 var fd = (new FormData(this.el.dom)).entries();
54142                 var ret = {};
54143                 var ent = fd.next();
54144                 while (!ent.done) {
54145                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54146                     ent = fd.next();
54147                 };
54148                 return ret;
54149             } catch(e) {
54150                 
54151             }
54152             
54153         }
54154         
54155         
54156         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54157         if(asString === true){
54158             return fs;
54159         }
54160         return Roo.urlDecode(fs);
54161     },
54162     
54163     /**
54164      * Returns the fields in this form as an object with key/value pairs. 
54165      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54166      * Normally this will not return readOnly data 
54167      * @param {Boolean} with_readonly return readonly field data.
54168      * @return {Object}
54169      */
54170     getFieldValues : function(with_readonly)
54171     {
54172         if (this.childForms) {
54173             // copy values from the child forms
54174             // should this call getFieldValues - probably not as we do not currently copy
54175             // hidden fields when we generate..
54176             Roo.each(this.childForms, function (f) {
54177                 this.setValues(f.getFieldValues());
54178             }, this);
54179         }
54180         
54181         var ret = {};
54182         this.items.each(function(f){
54183             
54184             if (f.readOnly && with_readonly !== true) {
54185                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54186                         // if a subform contains a copy of them.
54187                         // if you have subforms with the same editable data, you will need to copy the data back
54188                         // and forth.
54189             }
54190             
54191             if (!f.getName()) {
54192                 return;
54193             }
54194             var v = f.getValue();
54195             if (f.inputType =='radio') {
54196                 if (typeof(ret[f.getName()]) == 'undefined') {
54197                     ret[f.getName()] = ''; // empty..
54198                 }
54199                 
54200                 if (!f.el.dom.checked) {
54201                     return;
54202                     
54203                 }
54204                 v = f.el.dom.value;
54205                 
54206             }
54207             
54208             // not sure if this supported any more..
54209             if ((typeof(v) == 'object') && f.getRawValue) {
54210                 v = f.getRawValue() ; // dates..
54211             }
54212             // combo boxes where name != hiddenName...
54213             if (f.name != f.getName()) {
54214                 ret[f.name] = f.getRawValue();
54215             }
54216             ret[f.getName()] = v;
54217         });
54218         
54219         return ret;
54220     },
54221
54222     /**
54223      * Clears all invalid messages in this form.
54224      * @return {BasicForm} this
54225      */
54226     clearInvalid : function(){
54227         this.items.each(function(f){
54228            f.clearInvalid();
54229         });
54230         
54231         Roo.each(this.childForms || [], function (f) {
54232             f.clearInvalid();
54233         });
54234         
54235         
54236         return this;
54237     },
54238
54239     /**
54240      * Resets this form.
54241      * @return {BasicForm} this
54242      */
54243     reset : function(){
54244         this.items.each(function(f){
54245             f.reset();
54246         });
54247         
54248         Roo.each(this.childForms || [], function (f) {
54249             f.reset();
54250         });
54251         this.resetHasChanged();
54252         
54253         return this;
54254     },
54255
54256     /**
54257      * Add Roo.form components to this form.
54258      * @param {Field} field1
54259      * @param {Field} field2 (optional)
54260      * @param {Field} etc (optional)
54261      * @return {BasicForm} this
54262      */
54263     add : function(){
54264         this.items.addAll(Array.prototype.slice.call(arguments, 0));
54265         return this;
54266     },
54267
54268
54269     /**
54270      * Removes a field from the items collection (does NOT remove its markup).
54271      * @param {Field} field
54272      * @return {BasicForm} this
54273      */
54274     remove : function(field){
54275         this.items.remove(field);
54276         return this;
54277     },
54278
54279     /**
54280      * Looks at the fields in this form, checks them for an id attribute,
54281      * and calls applyTo on the existing dom element with that id.
54282      * @return {BasicForm} this
54283      */
54284     render : function(){
54285         this.items.each(function(f){
54286             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54287                 f.applyTo(f.id);
54288             }
54289         });
54290         return this;
54291     },
54292
54293     /**
54294      * Calls {@link Ext#apply} for all fields in this form with the passed object.
54295      * @param {Object} values
54296      * @return {BasicForm} this
54297      */
54298     applyToFields : function(o){
54299         this.items.each(function(f){
54300            Roo.apply(f, o);
54301         });
54302         return this;
54303     },
54304
54305     /**
54306      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54307      * @param {Object} values
54308      * @return {BasicForm} this
54309      */
54310     applyIfToFields : function(o){
54311         this.items.each(function(f){
54312            Roo.applyIf(f, o);
54313         });
54314         return this;
54315     }
54316 });
54317
54318 // back compat
54319 Roo.BasicForm = Roo.form.BasicForm;
54320
54321 Roo.apply(Roo.form.BasicForm, {
54322     
54323     popover : {
54324         
54325         padding : 5,
54326         
54327         isApplied : false,
54328         
54329         isMasked : false,
54330         
54331         form : false,
54332         
54333         target : false,
54334         
54335         intervalID : false,
54336         
54337         maskEl : false,
54338         
54339         apply : function()
54340         {
54341             if(this.isApplied){
54342                 return;
54343             }
54344             
54345             this.maskEl = {
54346                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54347                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54348                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54349                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54350             };
54351             
54352             this.maskEl.top.enableDisplayMode("block");
54353             this.maskEl.left.enableDisplayMode("block");
54354             this.maskEl.bottom.enableDisplayMode("block");
54355             this.maskEl.right.enableDisplayMode("block");
54356             
54357             Roo.get(document.body).on('click', function(){
54358                 this.unmask();
54359             }, this);
54360             
54361             Roo.get(document.body).on('touchstart', function(){
54362                 this.unmask();
54363             }, this);
54364             
54365             this.isApplied = true
54366         },
54367         
54368         mask : function(form, target)
54369         {
54370             this.form = form;
54371             
54372             this.target = target;
54373             
54374             if(!this.form.errorMask || !target.el){
54375                 return;
54376             }
54377             
54378             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54379             
54380             var ot = this.target.el.calcOffsetsTo(scrollable);
54381             
54382             var scrollTo = ot[1] - this.form.maskOffset;
54383             
54384             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54385             
54386             scrollable.scrollTo('top', scrollTo);
54387             
54388             var el = this.target.wrap || this.target.el;
54389             
54390             var box = el.getBox();
54391             
54392             this.maskEl.top.setStyle('position', 'absolute');
54393             this.maskEl.top.setStyle('z-index', 10000);
54394             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54395             this.maskEl.top.setLeft(0);
54396             this.maskEl.top.setTop(0);
54397             this.maskEl.top.show();
54398             
54399             this.maskEl.left.setStyle('position', 'absolute');
54400             this.maskEl.left.setStyle('z-index', 10000);
54401             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54402             this.maskEl.left.setLeft(0);
54403             this.maskEl.left.setTop(box.y - this.padding);
54404             this.maskEl.left.show();
54405
54406             this.maskEl.bottom.setStyle('position', 'absolute');
54407             this.maskEl.bottom.setStyle('z-index', 10000);
54408             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54409             this.maskEl.bottom.setLeft(0);
54410             this.maskEl.bottom.setTop(box.bottom + this.padding);
54411             this.maskEl.bottom.show();
54412
54413             this.maskEl.right.setStyle('position', 'absolute');
54414             this.maskEl.right.setStyle('z-index', 10000);
54415             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54416             this.maskEl.right.setLeft(box.right + this.padding);
54417             this.maskEl.right.setTop(box.y - this.padding);
54418             this.maskEl.right.show();
54419
54420             this.intervalID = window.setInterval(function() {
54421                 Roo.form.BasicForm.popover.unmask();
54422             }, 10000);
54423
54424             window.onwheel = function(){ return false;};
54425             
54426             (function(){ this.isMasked = true; }).defer(500, this);
54427             
54428         },
54429         
54430         unmask : function()
54431         {
54432             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54433                 return;
54434             }
54435             
54436             this.maskEl.top.setStyle('position', 'absolute');
54437             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54438             this.maskEl.top.hide();
54439
54440             this.maskEl.left.setStyle('position', 'absolute');
54441             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54442             this.maskEl.left.hide();
54443
54444             this.maskEl.bottom.setStyle('position', 'absolute');
54445             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54446             this.maskEl.bottom.hide();
54447
54448             this.maskEl.right.setStyle('position', 'absolute');
54449             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54450             this.maskEl.right.hide();
54451             
54452             window.onwheel = function(){ return true;};
54453             
54454             if(this.intervalID){
54455                 window.clearInterval(this.intervalID);
54456                 this.intervalID = false;
54457             }
54458             
54459             this.isMasked = false;
54460             
54461         }
54462         
54463     }
54464     
54465 });/*
54466  * Based on:
54467  * Ext JS Library 1.1.1
54468  * Copyright(c) 2006-2007, Ext JS, LLC.
54469  *
54470  * Originally Released Under LGPL - original licence link has changed is not relivant.
54471  *
54472  * Fork - LGPL
54473  * <script type="text/javascript">
54474  */
54475
54476 /**
54477  * @class Roo.form.Form
54478  * @extends Roo.form.BasicForm
54479  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54480  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54481  * @constructor
54482  * @param {Object} config Configuration options
54483  */
54484 Roo.form.Form = function(config){
54485     var xitems =  [];
54486     if (config.items) {
54487         xitems = config.items;
54488         delete config.items;
54489     }
54490    
54491     
54492     Roo.form.Form.superclass.constructor.call(this, null, config);
54493     this.url = this.url || this.action;
54494     if(!this.root){
54495         this.root = new Roo.form.Layout(Roo.applyIf({
54496             id: Roo.id()
54497         }, config));
54498     }
54499     this.active = this.root;
54500     /**
54501      * Array of all the buttons that have been added to this form via {@link addButton}
54502      * @type Array
54503      */
54504     this.buttons = [];
54505     this.allItems = [];
54506     this.addEvents({
54507         /**
54508          * @event clientvalidation
54509          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54510          * @param {Form} this
54511          * @param {Boolean} valid true if the form has passed client-side validation
54512          */
54513         clientvalidation: true,
54514         /**
54515          * @event rendered
54516          * Fires when the form is rendered
54517          * @param {Roo.form.Form} form
54518          */
54519         rendered : true
54520     });
54521     
54522     if (this.progressUrl) {
54523             // push a hidden field onto the list of fields..
54524             this.addxtype( {
54525                     xns: Roo.form, 
54526                     xtype : 'Hidden', 
54527                     name : 'UPLOAD_IDENTIFIER' 
54528             });
54529         }
54530         
54531     
54532     Roo.each(xitems, this.addxtype, this);
54533     
54534 };
54535
54536 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54537      /**
54538      * @cfg {Roo.Button} buttons[] buttons at bottom of form
54539      */
54540     
54541     /**
54542      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54543      */
54544     /**
54545      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54546      */
54547     /**
54548      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54549      */
54550     buttonAlign:'center',
54551
54552     /**
54553      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54554      */
54555     minButtonWidth:75,
54556
54557     /**
54558      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54559      * This property cascades to child containers if not set.
54560      */
54561     labelAlign:'left',
54562
54563     /**
54564      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54565      * fires a looping event with that state. This is required to bind buttons to the valid
54566      * state using the config value formBind:true on the button.
54567      */
54568     monitorValid : false,
54569
54570     /**
54571      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54572      */
54573     monitorPoll : 200,
54574     
54575     /**
54576      * @cfg {String} progressUrl - Url to return progress data 
54577      */
54578     
54579     progressUrl : false,
54580     /**
54581      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54582      * sending a formdata with extra parameters - eg uploaded elements.
54583      */
54584     
54585     formData : false,
54586     
54587     /**
54588      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54589      * fields are added and the column is closed. If no fields are passed the column remains open
54590      * until end() is called.
54591      * @param {Object} config The config to pass to the column
54592      * @param {Field} field1 (optional)
54593      * @param {Field} field2 (optional)
54594      * @param {Field} etc (optional)
54595      * @return Column The column container object
54596      */
54597     column : function(c){
54598         var col = new Roo.form.Column(c);
54599         this.start(col);
54600         if(arguments.length > 1){ // duplicate code required because of Opera
54601             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54602             this.end();
54603         }
54604         return col;
54605     },
54606
54607     /**
54608      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54609      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54610      * until end() is called.
54611      * @param {Object} config The config to pass to the fieldset
54612      * @param {Field} field1 (optional)
54613      * @param {Field} field2 (optional)
54614      * @param {Field} etc (optional)
54615      * @return FieldSet The fieldset container object
54616      */
54617     fieldset : function(c){
54618         var fs = new Roo.form.FieldSet(c);
54619         this.start(fs);
54620         if(arguments.length > 1){ // duplicate code required because of Opera
54621             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54622             this.end();
54623         }
54624         return fs;
54625     },
54626
54627     /**
54628      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54629      * fields are added and the container is closed. If no fields are passed the container remains open
54630      * until end() is called.
54631      * @param {Object} config The config to pass to the Layout
54632      * @param {Field} field1 (optional)
54633      * @param {Field} field2 (optional)
54634      * @param {Field} etc (optional)
54635      * @return Layout The container object
54636      */
54637     container : function(c){
54638         var l = new Roo.form.Layout(c);
54639         this.start(l);
54640         if(arguments.length > 1){ // duplicate code required because of Opera
54641             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54642             this.end();
54643         }
54644         return l;
54645     },
54646
54647     /**
54648      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54649      * @param {Object} container A Roo.form.Layout or subclass of Layout
54650      * @return {Form} this
54651      */
54652     start : function(c){
54653         // cascade label info
54654         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54655         this.active.stack.push(c);
54656         c.ownerCt = this.active;
54657         this.active = c;
54658         return this;
54659     },
54660
54661     /**
54662      * Closes the current open container
54663      * @return {Form} this
54664      */
54665     end : function(){
54666         if(this.active == this.root){
54667             return this;
54668         }
54669         this.active = this.active.ownerCt;
54670         return this;
54671     },
54672
54673     /**
54674      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
54675      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54676      * as the label of the field.
54677      * @param {Field} field1
54678      * @param {Field} field2 (optional)
54679      * @param {Field} etc. (optional)
54680      * @return {Form} this
54681      */
54682     add : function(){
54683         this.active.stack.push.apply(this.active.stack, arguments);
54684         this.allItems.push.apply(this.allItems,arguments);
54685         var r = [];
54686         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54687             if(a[i].isFormField){
54688                 r.push(a[i]);
54689             }
54690         }
54691         if(r.length > 0){
54692             Roo.form.Form.superclass.add.apply(this, r);
54693         }
54694         return this;
54695     },
54696     
54697
54698     
54699     
54700     
54701      /**
54702      * Find any element that has been added to a form, using it's ID or name
54703      * This can include framesets, columns etc. along with regular fields..
54704      * @param {String} id - id or name to find.
54705      
54706      * @return {Element} e - or false if nothing found.
54707      */
54708     findbyId : function(id)
54709     {
54710         var ret = false;
54711         if (!id) {
54712             return ret;
54713         }
54714         Roo.each(this.allItems, function(f){
54715             if (f.id == id || f.name == id ){
54716                 ret = f;
54717                 return false;
54718             }
54719         });
54720         return ret;
54721     },
54722
54723     
54724     
54725     /**
54726      * Render this form into the passed container. This should only be called once!
54727      * @param {String/HTMLElement/Element} container The element this component should be rendered into
54728      * @return {Form} this
54729      */
54730     render : function(ct)
54731     {
54732         
54733         
54734         
54735         ct = Roo.get(ct);
54736         var o = this.autoCreate || {
54737             tag: 'form',
54738             method : this.method || 'POST',
54739             id : this.id || Roo.id()
54740         };
54741         this.initEl(ct.createChild(o));
54742
54743         this.root.render(this.el);
54744         
54745        
54746              
54747         this.items.each(function(f){
54748             f.render('x-form-el-'+f.id);
54749         });
54750
54751         if(this.buttons.length > 0){
54752             // tables are required to maintain order and for correct IE layout
54753             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
54754                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
54755                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
54756             }}, null, true);
54757             var tr = tb.getElementsByTagName('tr')[0];
54758             for(var i = 0, len = this.buttons.length; i < len; i++) {
54759                 var b = this.buttons[i];
54760                 var td = document.createElement('td');
54761                 td.className = 'x-form-btn-td';
54762                 b.render(tr.appendChild(td));
54763             }
54764         }
54765         if(this.monitorValid){ // initialize after render
54766             this.startMonitoring();
54767         }
54768         this.fireEvent('rendered', this);
54769         return this;
54770     },
54771
54772     /**
54773      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
54774      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
54775      * object or a valid Roo.DomHelper element config
54776      * @param {Function} handler The function called when the button is clicked
54777      * @param {Object} scope (optional) The scope of the handler function
54778      * @return {Roo.Button}
54779      */
54780     addButton : function(config, handler, scope){
54781         var bc = {
54782             handler: handler,
54783             scope: scope,
54784             minWidth: this.minButtonWidth,
54785             hideParent:true
54786         };
54787         if(typeof config == "string"){
54788             bc.text = config;
54789         }else{
54790             Roo.apply(bc, config);
54791         }
54792         var btn = new Roo.Button(null, bc);
54793         this.buttons.push(btn);
54794         return btn;
54795     },
54796
54797      /**
54798      * Adds a series of form elements (using the xtype property as the factory method.
54799      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
54800      * @param {Object} config 
54801      */
54802     
54803     addxtype : function()
54804     {
54805         var ar = Array.prototype.slice.call(arguments, 0);
54806         var ret = false;
54807         for(var i = 0; i < ar.length; i++) {
54808             if (!ar[i]) {
54809                 continue; // skip -- if this happends something invalid got sent, we 
54810                 // should ignore it, as basically that interface element will not show up
54811                 // and that should be pretty obvious!!
54812             }
54813             
54814             if (Roo.form[ar[i].xtype]) {
54815                 ar[i].form = this;
54816                 var fe = Roo.factory(ar[i], Roo.form);
54817                 if (!ret) {
54818                     ret = fe;
54819                 }
54820                 fe.form = this;
54821                 if (fe.store) {
54822                     fe.store.form = this;
54823                 }
54824                 if (fe.isLayout) {  
54825                          
54826                     this.start(fe);
54827                     this.allItems.push(fe);
54828                     if (fe.items && fe.addxtype) {
54829                         fe.addxtype.apply(fe, fe.items);
54830                         delete fe.items;
54831                     }
54832                      this.end();
54833                     continue;
54834                 }
54835                 
54836                 
54837                  
54838                 this.add(fe);
54839               //  console.log('adding ' + ar[i].xtype);
54840             }
54841             if (ar[i].xtype == 'Button') {  
54842                 //console.log('adding button');
54843                 //console.log(ar[i]);
54844                 this.addButton(ar[i]);
54845                 this.allItems.push(fe);
54846                 continue;
54847             }
54848             
54849             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
54850                 alert('end is not supported on xtype any more, use items');
54851             //    this.end();
54852             //    //console.log('adding end');
54853             }
54854             
54855         }
54856         return ret;
54857     },
54858     
54859     /**
54860      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
54861      * option "monitorValid"
54862      */
54863     startMonitoring : function(){
54864         if(!this.bound){
54865             this.bound = true;
54866             Roo.TaskMgr.start({
54867                 run : this.bindHandler,
54868                 interval : this.monitorPoll || 200,
54869                 scope: this
54870             });
54871         }
54872     },
54873
54874     /**
54875      * Stops monitoring of the valid state of this form
54876      */
54877     stopMonitoring : function(){
54878         this.bound = false;
54879     },
54880
54881     // private
54882     bindHandler : function(){
54883         if(!this.bound){
54884             return false; // stops binding
54885         }
54886         var valid = true;
54887         this.items.each(function(f){
54888             if(!f.isValid(true)){
54889                 valid = false;
54890                 return false;
54891             }
54892         });
54893         for(var i = 0, len = this.buttons.length; i < len; i++){
54894             var btn = this.buttons[i];
54895             if(btn.formBind === true && btn.disabled === valid){
54896                 btn.setDisabled(!valid);
54897             }
54898         }
54899         this.fireEvent('clientvalidation', this, valid);
54900     }
54901     
54902     
54903     
54904     
54905     
54906     
54907     
54908     
54909 });
54910
54911
54912 // back compat
54913 Roo.Form = Roo.form.Form;
54914 /*
54915  * Based on:
54916  * Ext JS Library 1.1.1
54917  * Copyright(c) 2006-2007, Ext JS, LLC.
54918  *
54919  * Originally Released Under LGPL - original licence link has changed is not relivant.
54920  *
54921  * Fork - LGPL
54922  * <script type="text/javascript">
54923  */
54924
54925 // as we use this in bootstrap.
54926 Roo.namespace('Roo.form');
54927  /**
54928  * @class Roo.form.Action
54929  * Internal Class used to handle form actions
54930  * @constructor
54931  * @param {Roo.form.BasicForm} el The form element or its id
54932  * @param {Object} config Configuration options
54933  */
54934
54935  
54936  
54937 // define the action interface
54938 Roo.form.Action = function(form, options){
54939     this.form = form;
54940     this.options = options || {};
54941 };
54942 /**
54943  * Client Validation Failed
54944  * @const 
54945  */
54946 Roo.form.Action.CLIENT_INVALID = 'client';
54947 /**
54948  * Server Validation Failed
54949  * @const 
54950  */
54951 Roo.form.Action.SERVER_INVALID = 'server';
54952  /**
54953  * Connect to Server Failed
54954  * @const 
54955  */
54956 Roo.form.Action.CONNECT_FAILURE = 'connect';
54957 /**
54958  * Reading Data from Server Failed
54959  * @const 
54960  */
54961 Roo.form.Action.LOAD_FAILURE = 'load';
54962
54963 Roo.form.Action.prototype = {
54964     type : 'default',
54965     failureType : undefined,
54966     response : undefined,
54967     result : undefined,
54968
54969     // interface method
54970     run : function(options){
54971
54972     },
54973
54974     // interface method
54975     success : function(response){
54976
54977     },
54978
54979     // interface method
54980     handleResponse : function(response){
54981
54982     },
54983
54984     // default connection failure
54985     failure : function(response){
54986         
54987         this.response = response;
54988         this.failureType = Roo.form.Action.CONNECT_FAILURE;
54989         this.form.afterAction(this, false);
54990     },
54991
54992     processResponse : function(response){
54993         this.response = response;
54994         if(!response.responseText){
54995             return true;
54996         }
54997         this.result = this.handleResponse(response);
54998         return this.result;
54999     },
55000
55001     // utility functions used internally
55002     getUrl : function(appendParams){
55003         var url = this.options.url || this.form.url || this.form.el.dom.action;
55004         if(appendParams){
55005             var p = this.getParams();
55006             if(p){
55007                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55008             }
55009         }
55010         return url;
55011     },
55012
55013     getMethod : function(){
55014         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55015     },
55016
55017     getParams : function(){
55018         var bp = this.form.baseParams;
55019         var p = this.options.params;
55020         if(p){
55021             if(typeof p == "object"){
55022                 p = Roo.urlEncode(Roo.applyIf(p, bp));
55023             }else if(typeof p == 'string' && bp){
55024                 p += '&' + Roo.urlEncode(bp);
55025             }
55026         }else if(bp){
55027             p = Roo.urlEncode(bp);
55028         }
55029         return p;
55030     },
55031
55032     createCallback : function(){
55033         return {
55034             success: this.success,
55035             failure: this.failure,
55036             scope: this,
55037             timeout: (this.form.timeout*1000),
55038             upload: this.form.fileUpload ? this.success : undefined
55039         };
55040     }
55041 };
55042
55043 Roo.form.Action.Submit = function(form, options){
55044     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55045 };
55046
55047 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55048     type : 'submit',
55049
55050     haveProgress : false,
55051     uploadComplete : false,
55052     
55053     // uploadProgress indicator.
55054     uploadProgress : function()
55055     {
55056         if (!this.form.progressUrl) {
55057             return;
55058         }
55059         
55060         if (!this.haveProgress) {
55061             Roo.MessageBox.progress("Uploading", "Uploading");
55062         }
55063         if (this.uploadComplete) {
55064            Roo.MessageBox.hide();
55065            return;
55066         }
55067         
55068         this.haveProgress = true;
55069    
55070         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55071         
55072         var c = new Roo.data.Connection();
55073         c.request({
55074             url : this.form.progressUrl,
55075             params: {
55076                 id : uid
55077             },
55078             method: 'GET',
55079             success : function(req){
55080                //console.log(data);
55081                 var rdata = false;
55082                 var edata;
55083                 try  {
55084                    rdata = Roo.decode(req.responseText)
55085                 } catch (e) {
55086                     Roo.log("Invalid data from server..");
55087                     Roo.log(edata);
55088                     return;
55089                 }
55090                 if (!rdata || !rdata.success) {
55091                     Roo.log(rdata);
55092                     Roo.MessageBox.alert(Roo.encode(rdata));
55093                     return;
55094                 }
55095                 var data = rdata.data;
55096                 
55097                 if (this.uploadComplete) {
55098                    Roo.MessageBox.hide();
55099                    return;
55100                 }
55101                    
55102                 if (data){
55103                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55104                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55105                     );
55106                 }
55107                 this.uploadProgress.defer(2000,this);
55108             },
55109        
55110             failure: function(data) {
55111                 Roo.log('progress url failed ');
55112                 Roo.log(data);
55113             },
55114             scope : this
55115         });
55116            
55117     },
55118     
55119     
55120     run : function()
55121     {
55122         // run get Values on the form, so it syncs any secondary forms.
55123         this.form.getValues();
55124         
55125         var o = this.options;
55126         var method = this.getMethod();
55127         var isPost = method == 'POST';
55128         if(o.clientValidation === false || this.form.isValid()){
55129             
55130             if (this.form.progressUrl) {
55131                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55132                     (new Date() * 1) + '' + Math.random());
55133                     
55134             } 
55135             
55136             
55137             Roo.Ajax.request(Roo.apply(this.createCallback(), {
55138                 form:this.form.el.dom,
55139                 url:this.getUrl(!isPost),
55140                 method: method,
55141                 params:isPost ? this.getParams() : null,
55142                 isUpload: this.form.fileUpload,
55143                 formData : this.form.formData
55144             }));
55145             
55146             this.uploadProgress();
55147
55148         }else if (o.clientValidation !== false){ // client validation failed
55149             this.failureType = Roo.form.Action.CLIENT_INVALID;
55150             this.form.afterAction(this, false);
55151         }
55152     },
55153
55154     success : function(response)
55155     {
55156         this.uploadComplete= true;
55157         if (this.haveProgress) {
55158             Roo.MessageBox.hide();
55159         }
55160         
55161         
55162         var result = this.processResponse(response);
55163         if(result === true || result.success){
55164             this.form.afterAction(this, true);
55165             return;
55166         }
55167         if(result.errors){
55168             this.form.markInvalid(result.errors);
55169             this.failureType = Roo.form.Action.SERVER_INVALID;
55170         }
55171         this.form.afterAction(this, false);
55172     },
55173     failure : function(response)
55174     {
55175         this.uploadComplete= true;
55176         if (this.haveProgress) {
55177             Roo.MessageBox.hide();
55178         }
55179         
55180         this.response = response;
55181         this.failureType = Roo.form.Action.CONNECT_FAILURE;
55182         this.form.afterAction(this, false);
55183     },
55184     
55185     handleResponse : function(response){
55186         if(this.form.errorReader){
55187             var rs = this.form.errorReader.read(response);
55188             var errors = [];
55189             if(rs.records){
55190                 for(var i = 0, len = rs.records.length; i < len; i++) {
55191                     var r = rs.records[i];
55192                     errors[i] = r.data;
55193                 }
55194             }
55195             if(errors.length < 1){
55196                 errors = null;
55197             }
55198             return {
55199                 success : rs.success,
55200                 errors : errors
55201             };
55202         }
55203         var ret = false;
55204         try {
55205             ret = Roo.decode(response.responseText);
55206         } catch (e) {
55207             ret = {
55208                 success: false,
55209                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55210                 errors : []
55211             };
55212         }
55213         return ret;
55214         
55215     }
55216 });
55217
55218
55219 Roo.form.Action.Load = function(form, options){
55220     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55221     this.reader = this.form.reader;
55222 };
55223
55224 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55225     type : 'load',
55226
55227     run : function(){
55228         
55229         Roo.Ajax.request(Roo.apply(
55230                 this.createCallback(), {
55231                     method:this.getMethod(),
55232                     url:this.getUrl(false),
55233                     params:this.getParams()
55234         }));
55235     },
55236
55237     success : function(response){
55238         
55239         var result = this.processResponse(response);
55240         if(result === true || !result.success || !result.data){
55241             this.failureType = Roo.form.Action.LOAD_FAILURE;
55242             this.form.afterAction(this, false);
55243             return;
55244         }
55245         this.form.clearInvalid();
55246         this.form.setValues(result.data);
55247         this.form.afterAction(this, true);
55248     },
55249
55250     handleResponse : function(response){
55251         if(this.form.reader){
55252             var rs = this.form.reader.read(response);
55253             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55254             return {
55255                 success : rs.success,
55256                 data : data
55257             };
55258         }
55259         return Roo.decode(response.responseText);
55260     }
55261 });
55262
55263 Roo.form.Action.ACTION_TYPES = {
55264     'load' : Roo.form.Action.Load,
55265     'submit' : Roo.form.Action.Submit
55266 };/*
55267  * Based on:
55268  * Ext JS Library 1.1.1
55269  * Copyright(c) 2006-2007, Ext JS, LLC.
55270  *
55271  * Originally Released Under LGPL - original licence link has changed is not relivant.
55272  *
55273  * Fork - LGPL
55274  * <script type="text/javascript">
55275  */
55276  
55277 /**
55278  * @class Roo.form.Layout
55279  * @extends Roo.Component
55280  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55281  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55282  * @constructor
55283  * @param {Object} config Configuration options
55284  */
55285 Roo.form.Layout = function(config){
55286     var xitems = [];
55287     if (config.items) {
55288         xitems = config.items;
55289         delete config.items;
55290     }
55291     Roo.form.Layout.superclass.constructor.call(this, config);
55292     this.stack = [];
55293     Roo.each(xitems, this.addxtype, this);
55294      
55295 };
55296
55297 Roo.extend(Roo.form.Layout, Roo.Component, {
55298     /**
55299      * @cfg {String/Object} autoCreate
55300      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55301      */
55302     /**
55303      * @cfg {String/Object/Function} style
55304      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55305      * a function which returns such a specification.
55306      */
55307     /**
55308      * @cfg {String} labelAlign (left|top|right)
55309      * Valid values are "left," "top" and "right" (defaults to "left")
55310      */
55311     /**
55312      * @cfg {Number} labelWidth
55313      * Fixed width in pixels of all field labels (defaults to undefined)
55314      */
55315     /**
55316      * @cfg {Boolean} clear
55317      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55318      */
55319     clear : true,
55320     /**
55321      * @cfg {String} labelSeparator
55322      * The separator to use after field labels (defaults to ':')
55323      */
55324     labelSeparator : ':',
55325     /**
55326      * @cfg {Boolean} hideLabels
55327      * True to suppress the display of field labels in this layout (defaults to false)
55328      */
55329     hideLabels : false,
55330
55331     // private
55332     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55333     
55334     isLayout : true,
55335     
55336     // private
55337     onRender : function(ct, position){
55338         if(this.el){ // from markup
55339             this.el = Roo.get(this.el);
55340         }else {  // generate
55341             var cfg = this.getAutoCreate();
55342             this.el = ct.createChild(cfg, position);
55343         }
55344         if(this.style){
55345             this.el.applyStyles(this.style);
55346         }
55347         if(this.labelAlign){
55348             this.el.addClass('x-form-label-'+this.labelAlign);
55349         }
55350         if(this.hideLabels){
55351             this.labelStyle = "display:none";
55352             this.elementStyle = "padding-left:0;";
55353         }else{
55354             if(typeof this.labelWidth == 'number'){
55355                 this.labelStyle = "width:"+this.labelWidth+"px;";
55356                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55357             }
55358             if(this.labelAlign == 'top'){
55359                 this.labelStyle = "width:auto;";
55360                 this.elementStyle = "padding-left:0;";
55361             }
55362         }
55363         var stack = this.stack;
55364         var slen = stack.length;
55365         if(slen > 0){
55366             if(!this.fieldTpl){
55367                 var t = new Roo.Template(
55368                     '<div class="x-form-item {5}">',
55369                         '<label for="{0}" style="{2}">{1}{4}</label>',
55370                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55371                         '</div>',
55372                     '</div><div class="x-form-clear-left"></div>'
55373                 );
55374                 t.disableFormats = true;
55375                 t.compile();
55376                 Roo.form.Layout.prototype.fieldTpl = t;
55377             }
55378             for(var i = 0; i < slen; i++) {
55379                 if(stack[i].isFormField){
55380                     this.renderField(stack[i]);
55381                 }else{
55382                     this.renderComponent(stack[i]);
55383                 }
55384             }
55385         }
55386         if(this.clear){
55387             this.el.createChild({cls:'x-form-clear'});
55388         }
55389     },
55390
55391     // private
55392     renderField : function(f){
55393         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55394                f.id, //0
55395                f.fieldLabel, //1
55396                f.labelStyle||this.labelStyle||'', //2
55397                this.elementStyle||'', //3
55398                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55399                f.itemCls||this.itemCls||''  //5
55400        ], true).getPrevSibling());
55401     },
55402
55403     // private
55404     renderComponent : function(c){
55405         c.render(c.isLayout ? this.el : this.el.createChild());    
55406     },
55407     /**
55408      * Adds a object form elements (using the xtype property as the factory method.)
55409      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
55410      * @param {Object} config 
55411      */
55412     addxtype : function(o)
55413     {
55414         // create the lement.
55415         o.form = this.form;
55416         var fe = Roo.factory(o, Roo.form);
55417         this.form.allItems.push(fe);
55418         this.stack.push(fe);
55419         
55420         if (fe.isFormField) {
55421             this.form.items.add(fe);
55422         }
55423          
55424         return fe;
55425     }
55426 });
55427
55428
55429 /**
55430  * @class Roo.form.Column
55431  * @extends Roo.form.Layout
55432  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55433  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55434  * @constructor
55435  * @param {Object} config Configuration options
55436  */
55437 Roo.form.Column = function(config){
55438     Roo.form.Column.superclass.constructor.call(this, config);
55439 };
55440
55441 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55442     /**
55443      * @cfg {Number/String} width
55444      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55445      */
55446     /**
55447      * @cfg {String/Object} autoCreate
55448      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55449      */
55450
55451     // private
55452     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55453
55454     // private
55455     onRender : function(ct, position){
55456         Roo.form.Column.superclass.onRender.call(this, ct, position);
55457         if(this.width){
55458             this.el.setWidth(this.width);
55459         }
55460     }
55461 });
55462
55463 /**
55464  * @class Roo.form.Row
55465  * @extends Roo.form.Layout
55466  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55467  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55468  * @constructor
55469  * @param {Object} config Configuration options
55470  */
55471
55472  
55473 Roo.form.Row = function(config){
55474     Roo.form.Row.superclass.constructor.call(this, config);
55475 };
55476  
55477 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55478       /**
55479      * @cfg {Number/String} width
55480      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55481      */
55482     /**
55483      * @cfg {Number/String} height
55484      * The fixed height of the column in pixels or CSS value (defaults to "auto")
55485      */
55486     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55487     
55488     padWidth : 20,
55489     // private
55490     onRender : function(ct, position){
55491         //console.log('row render');
55492         if(!this.rowTpl){
55493             var t = new Roo.Template(
55494                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55495                     '<label for="{0}" style="{2}">{1}{4}</label>',
55496                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55497                     '</div>',
55498                 '</div>'
55499             );
55500             t.disableFormats = true;
55501             t.compile();
55502             Roo.form.Layout.prototype.rowTpl = t;
55503         }
55504         this.fieldTpl = this.rowTpl;
55505         
55506         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55507         var labelWidth = 100;
55508         
55509         if ((this.labelAlign != 'top')) {
55510             if (typeof this.labelWidth == 'number') {
55511                 labelWidth = this.labelWidth
55512             }
55513             this.padWidth =  20 + labelWidth;
55514             
55515         }
55516         
55517         Roo.form.Column.superclass.onRender.call(this, ct, position);
55518         if(this.width){
55519             this.el.setWidth(this.width);
55520         }
55521         if(this.height){
55522             this.el.setHeight(this.height);
55523         }
55524     },
55525     
55526     // private
55527     renderField : function(f){
55528         f.fieldEl = this.fieldTpl.append(this.el, [
55529                f.id, f.fieldLabel,
55530                f.labelStyle||this.labelStyle||'',
55531                this.elementStyle||'',
55532                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55533                f.itemCls||this.itemCls||'',
55534                f.width ? f.width + this.padWidth : 160 + this.padWidth
55535        ],true);
55536     }
55537 });
55538  
55539
55540 /**
55541  * @class Roo.form.FieldSet
55542  * @extends Roo.form.Layout
55543  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55544  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55545  * @constructor
55546  * @param {Object} config Configuration options
55547  */
55548 Roo.form.FieldSet = function(config){
55549     Roo.form.FieldSet.superclass.constructor.call(this, config);
55550 };
55551
55552 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55553     /**
55554      * @cfg {String} legend
55555      * The text to display as the legend for the FieldSet (defaults to '')
55556      */
55557     /**
55558      * @cfg {String/Object} autoCreate
55559      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55560      */
55561
55562     // private
55563     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55564
55565     // private
55566     onRender : function(ct, position){
55567         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55568         if(this.legend){
55569             this.setLegend(this.legend);
55570         }
55571     },
55572
55573     // private
55574     setLegend : function(text){
55575         if(this.rendered){
55576             this.el.child('legend').update(text);
55577         }
55578     }
55579 });/*
55580  * Based on:
55581  * Ext JS Library 1.1.1
55582  * Copyright(c) 2006-2007, Ext JS, LLC.
55583  *
55584  * Originally Released Under LGPL - original licence link has changed is not relivant.
55585  *
55586  * Fork - LGPL
55587  * <script type="text/javascript">
55588  */
55589 /**
55590  * @class Roo.form.VTypes
55591  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55592  * @static
55593  */
55594 Roo.form.VTypes = function(){
55595     // closure these in so they are only created once.
55596     var alpha = /^[a-zA-Z_]+$/;
55597     var alphanum = /^[a-zA-Z0-9_]+$/;
55598     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55599     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55600
55601     // All these messages and functions are configurable
55602     return {
55603         /**
55604          * The function used to validate email addresses
55605          * @param {String} value The email address
55606          */
55607         'email' : function(v){
55608             return email.test(v);
55609         },
55610         /**
55611          * The error text to display when the email validation function returns false
55612          * @type String
55613          */
55614         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
55615         /**
55616          * The keystroke filter mask to be applied on email input
55617          * @type RegExp
55618          */
55619         'emailMask' : /[a-z0-9_\.\-@]/i,
55620
55621         /**
55622          * The function used to validate URLs
55623          * @param {String} value The URL
55624          */
55625         'url' : function(v){
55626             return url.test(v);
55627         },
55628         /**
55629          * The error text to display when the url validation function returns false
55630          * @type String
55631          */
55632         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55633         
55634         /**
55635          * The function used to validate alpha values
55636          * @param {String} value The value
55637          */
55638         'alpha' : function(v){
55639             return alpha.test(v);
55640         },
55641         /**
55642          * The error text to display when the alpha validation function returns false
55643          * @type String
55644          */
55645         'alphaText' : 'This field should only contain letters and _',
55646         /**
55647          * The keystroke filter mask to be applied on alpha input
55648          * @type RegExp
55649          */
55650         'alphaMask' : /[a-z_]/i,
55651
55652         /**
55653          * The function used to validate alphanumeric values
55654          * @param {String} value The value
55655          */
55656         'alphanum' : function(v){
55657             return alphanum.test(v);
55658         },
55659         /**
55660          * The error text to display when the alphanumeric validation function returns false
55661          * @type String
55662          */
55663         'alphanumText' : 'This field should only contain letters, numbers and _',
55664         /**
55665          * The keystroke filter mask to be applied on alphanumeric input
55666          * @type RegExp
55667          */
55668         'alphanumMask' : /[a-z0-9_]/i
55669     };
55670 }();//<script type="text/javascript">
55671
55672 /**
55673  * @class Roo.form.FCKeditor
55674  * @extends Roo.form.TextArea
55675  * Wrapper around the FCKEditor http://www.fckeditor.net
55676  * @constructor
55677  * Creates a new FCKeditor
55678  * @param {Object} config Configuration options
55679  */
55680 Roo.form.FCKeditor = function(config){
55681     Roo.form.FCKeditor.superclass.constructor.call(this, config);
55682     this.addEvents({
55683          /**
55684          * @event editorinit
55685          * Fired when the editor is initialized - you can add extra handlers here..
55686          * @param {FCKeditor} this
55687          * @param {Object} the FCK object.
55688          */
55689         editorinit : true
55690     });
55691     
55692     
55693 };
55694 Roo.form.FCKeditor.editors = { };
55695 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55696 {
55697     //defaultAutoCreate : {
55698     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
55699     //},
55700     // private
55701     /**
55702      * @cfg {Object} fck options - see fck manual for details.
55703      */
55704     fckconfig : false,
55705     
55706     /**
55707      * @cfg {Object} fck toolbar set (Basic or Default)
55708      */
55709     toolbarSet : 'Basic',
55710     /**
55711      * @cfg {Object} fck BasePath
55712      */ 
55713     basePath : '/fckeditor/',
55714     
55715     
55716     frame : false,
55717     
55718     value : '',
55719     
55720    
55721     onRender : function(ct, position)
55722     {
55723         if(!this.el){
55724             this.defaultAutoCreate = {
55725                 tag: "textarea",
55726                 style:"width:300px;height:60px;",
55727                 autocomplete: "new-password"
55728             };
55729         }
55730         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55731         /*
55732         if(this.grow){
55733             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55734             if(this.preventScrollbars){
55735                 this.el.setStyle("overflow", "hidden");
55736             }
55737             this.el.setHeight(this.growMin);
55738         }
55739         */
55740         //console.log('onrender' + this.getId() );
55741         Roo.form.FCKeditor.editors[this.getId()] = this;
55742          
55743
55744         this.replaceTextarea() ;
55745         
55746     },
55747     
55748     getEditor : function() {
55749         return this.fckEditor;
55750     },
55751     /**
55752      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
55753      * @param {Mixed} value The value to set
55754      */
55755     
55756     
55757     setValue : function(value)
55758     {
55759         //console.log('setValue: ' + value);
55760         
55761         if(typeof(value) == 'undefined') { // not sure why this is happending...
55762             return;
55763         }
55764         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55765         
55766         //if(!this.el || !this.getEditor()) {
55767         //    this.value = value;
55768             //this.setValue.defer(100,this,[value]);    
55769         //    return;
55770         //} 
55771         
55772         if(!this.getEditor()) {
55773             return;
55774         }
55775         
55776         this.getEditor().SetData(value);
55777         
55778         //
55779
55780     },
55781
55782     /**
55783      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
55784      * @return {Mixed} value The field value
55785      */
55786     getValue : function()
55787     {
55788         
55789         if (this.frame && this.frame.dom.style.display == 'none') {
55790             return Roo.form.FCKeditor.superclass.getValue.call(this);
55791         }
55792         
55793         if(!this.el || !this.getEditor()) {
55794            
55795            // this.getValue.defer(100,this); 
55796             return this.value;
55797         }
55798        
55799         
55800         var value=this.getEditor().GetData();
55801         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55802         return Roo.form.FCKeditor.superclass.getValue.call(this);
55803         
55804
55805     },
55806
55807     /**
55808      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
55809      * @return {Mixed} value The field value
55810      */
55811     getRawValue : function()
55812     {
55813         if (this.frame && this.frame.dom.style.display == 'none') {
55814             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55815         }
55816         
55817         if(!this.el || !this.getEditor()) {
55818             //this.getRawValue.defer(100,this); 
55819             return this.value;
55820             return;
55821         }
55822         
55823         
55824         
55825         var value=this.getEditor().GetData();
55826         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
55827         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55828          
55829     },
55830     
55831     setSize : function(w,h) {
55832         
55833         
55834         
55835         //if (this.frame && this.frame.dom.style.display == 'none') {
55836         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55837         //    return;
55838         //}
55839         //if(!this.el || !this.getEditor()) {
55840         //    this.setSize.defer(100,this, [w,h]); 
55841         //    return;
55842         //}
55843         
55844         
55845         
55846         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55847         
55848         this.frame.dom.setAttribute('width', w);
55849         this.frame.dom.setAttribute('height', h);
55850         this.frame.setSize(w,h);
55851         
55852     },
55853     
55854     toggleSourceEdit : function(value) {
55855         
55856       
55857          
55858         this.el.dom.style.display = value ? '' : 'none';
55859         this.frame.dom.style.display = value ?  'none' : '';
55860         
55861     },
55862     
55863     
55864     focus: function(tag)
55865     {
55866         if (this.frame.dom.style.display == 'none') {
55867             return Roo.form.FCKeditor.superclass.focus.call(this);
55868         }
55869         if(!this.el || !this.getEditor()) {
55870             this.focus.defer(100,this, [tag]); 
55871             return;
55872         }
55873         
55874         
55875         
55876         
55877         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
55878         this.getEditor().Focus();
55879         if (tgs.length) {
55880             if (!this.getEditor().Selection.GetSelection()) {
55881                 this.focus.defer(100,this, [tag]); 
55882                 return;
55883             }
55884             
55885             
55886             var r = this.getEditor().EditorDocument.createRange();
55887             r.setStart(tgs[0],0);
55888             r.setEnd(tgs[0],0);
55889             this.getEditor().Selection.GetSelection().removeAllRanges();
55890             this.getEditor().Selection.GetSelection().addRange(r);
55891             this.getEditor().Focus();
55892         }
55893         
55894     },
55895     
55896     
55897     
55898     replaceTextarea : function()
55899     {
55900         if ( document.getElementById( this.getId() + '___Frame' ) ) {
55901             return ;
55902         }
55903         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
55904         //{
55905             // We must check the elements firstly using the Id and then the name.
55906         var oTextarea = document.getElementById( this.getId() );
55907         
55908         var colElementsByName = document.getElementsByName( this.getId() ) ;
55909          
55910         oTextarea.style.display = 'none' ;
55911
55912         if ( oTextarea.tabIndex ) {            
55913             this.TabIndex = oTextarea.tabIndex ;
55914         }
55915         
55916         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
55917         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
55918         this.frame = Roo.get(this.getId() + '___Frame')
55919     },
55920     
55921     _getConfigHtml : function()
55922     {
55923         var sConfig = '' ;
55924
55925         for ( var o in this.fckconfig ) {
55926             sConfig += sConfig.length > 0  ? '&amp;' : '';
55927             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
55928         }
55929
55930         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
55931     },
55932     
55933     
55934     _getIFrameHtml : function()
55935     {
55936         var sFile = 'fckeditor.html' ;
55937         /* no idea what this is about..
55938         try
55939         {
55940             if ( (/fcksource=true/i).test( window.top.location.search ) )
55941                 sFile = 'fckeditor.original.html' ;
55942         }
55943         catch (e) { 
55944         */
55945
55946         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
55947         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
55948         
55949         
55950         var html = '<iframe id="' + this.getId() +
55951             '___Frame" src="' + sLink +
55952             '" width="' + this.width +
55953             '" height="' + this.height + '"' +
55954             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
55955             ' frameborder="0" scrolling="no"></iframe>' ;
55956
55957         return html ;
55958     },
55959     
55960     _insertHtmlBefore : function( html, element )
55961     {
55962         if ( element.insertAdjacentHTML )       {
55963             // IE
55964             element.insertAdjacentHTML( 'beforeBegin', html ) ;
55965         } else { // Gecko
55966             var oRange = document.createRange() ;
55967             oRange.setStartBefore( element ) ;
55968             var oFragment = oRange.createContextualFragment( html );
55969             element.parentNode.insertBefore( oFragment, element ) ;
55970         }
55971     }
55972     
55973     
55974   
55975     
55976     
55977     
55978     
55979
55980 });
55981
55982 //Roo.reg('fckeditor', Roo.form.FCKeditor);
55983
55984 function FCKeditor_OnComplete(editorInstance){
55985     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
55986     f.fckEditor = editorInstance;
55987     //console.log("loaded");
55988     f.fireEvent('editorinit', f, editorInstance);
55989
55990   
55991
55992  
55993
55994
55995
55996
55997
55998
55999
56000
56001
56002
56003
56004
56005
56006
56007
56008 //<script type="text/javascript">
56009 /**
56010  * @class Roo.form.GridField
56011  * @extends Roo.form.Field
56012  * Embed a grid (or editable grid into a form)
56013  * STATUS ALPHA
56014  * 
56015  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56016  * it needs 
56017  * xgrid.store = Roo.data.Store
56018  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56019  * xgrid.store.reader = Roo.data.JsonReader 
56020  * 
56021  * 
56022  * @constructor
56023  * Creates a new GridField
56024  * @param {Object} config Configuration options
56025  */
56026 Roo.form.GridField = function(config){
56027     Roo.form.GridField.superclass.constructor.call(this, config);
56028      
56029 };
56030
56031 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
56032     /**
56033      * @cfg {Number} width  - used to restrict width of grid..
56034      */
56035     width : 100,
56036     /**
56037      * @cfg {Number} height - used to restrict height of grid..
56038      */
56039     height : 50,
56040      /**
56041      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56042          * 
56043          *}
56044      */
56045     xgrid : false, 
56046     /**
56047      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56048      * {tag: "input", type: "checkbox", autocomplete: "off"})
56049      */
56050    // defaultAutoCreate : { tag: 'div' },
56051     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56052     /**
56053      * @cfg {String} addTitle Text to include for adding a title.
56054      */
56055     addTitle : false,
56056     //
56057     onResize : function(){
56058         Roo.form.Field.superclass.onResize.apply(this, arguments);
56059     },
56060
56061     initEvents : function(){
56062         // Roo.form.Checkbox.superclass.initEvents.call(this);
56063         // has no events...
56064        
56065     },
56066
56067
56068     getResizeEl : function(){
56069         return this.wrap;
56070     },
56071
56072     getPositionEl : function(){
56073         return this.wrap;
56074     },
56075
56076     // private
56077     onRender : function(ct, position){
56078         
56079         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56080         var style = this.style;
56081         delete this.style;
56082         
56083         Roo.form.GridField.superclass.onRender.call(this, ct, position);
56084         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56085         this.viewEl = this.wrap.createChild({ tag: 'div' });
56086         if (style) {
56087             this.viewEl.applyStyles(style);
56088         }
56089         if (this.width) {
56090             this.viewEl.setWidth(this.width);
56091         }
56092         if (this.height) {
56093             this.viewEl.setHeight(this.height);
56094         }
56095         //if(this.inputValue !== undefined){
56096         //this.setValue(this.value);
56097         
56098         
56099         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56100         
56101         
56102         this.grid.render();
56103         this.grid.getDataSource().on('remove', this.refreshValue, this);
56104         this.grid.getDataSource().on('update', this.refreshValue, this);
56105         this.grid.on('afteredit', this.refreshValue, this);
56106  
56107     },
56108      
56109     
56110     /**
56111      * Sets the value of the item. 
56112      * @param {String} either an object  or a string..
56113      */
56114     setValue : function(v){
56115         //this.value = v;
56116         v = v || []; // empty set..
56117         // this does not seem smart - it really only affects memoryproxy grids..
56118         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56119             var ds = this.grid.getDataSource();
56120             // assumes a json reader..
56121             var data = {}
56122             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
56123             ds.loadData( data);
56124         }
56125         // clear selection so it does not get stale.
56126         if (this.grid.sm) { 
56127             this.grid.sm.clearSelections();
56128         }
56129         
56130         Roo.form.GridField.superclass.setValue.call(this, v);
56131         this.refreshValue();
56132         // should load data in the grid really....
56133     },
56134     
56135     // private
56136     refreshValue: function() {
56137          var val = [];
56138         this.grid.getDataSource().each(function(r) {
56139             val.push(r.data);
56140         });
56141         this.el.dom.value = Roo.encode(val);
56142     }
56143     
56144      
56145     
56146     
56147 });/*
56148  * Based on:
56149  * Ext JS Library 1.1.1
56150  * Copyright(c) 2006-2007, Ext JS, LLC.
56151  *
56152  * Originally Released Under LGPL - original licence link has changed is not relivant.
56153  *
56154  * Fork - LGPL
56155  * <script type="text/javascript">
56156  */
56157 /**
56158  * @class Roo.form.DisplayField
56159  * @extends Roo.form.Field
56160  * A generic Field to display non-editable data.
56161  * @cfg {Boolean} closable (true|false) default false
56162  * @constructor
56163  * Creates a new Display Field item.
56164  * @param {Object} config Configuration options
56165  */
56166 Roo.form.DisplayField = function(config){
56167     Roo.form.DisplayField.superclass.constructor.call(this, config);
56168     
56169     this.addEvents({
56170         /**
56171          * @event close
56172          * Fires after the click the close btn
56173              * @param {Roo.form.DisplayField} this
56174              */
56175         close : true
56176     });
56177 };
56178
56179 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
56180     inputType:      'hidden',
56181     allowBlank:     true,
56182     readOnly:         true,
56183     
56184  
56185     /**
56186      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56187      */
56188     focusClass : undefined,
56189     /**
56190      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56191      */
56192     fieldClass: 'x-form-field',
56193     
56194      /**
56195      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56196      */
56197     valueRenderer: undefined,
56198     
56199     width: 100,
56200     /**
56201      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56202      * {tag: "input", type: "checkbox", autocomplete: "off"})
56203      */
56204      
56205  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56206  
56207     closable : false,
56208     
56209     onResize : function(){
56210         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56211         
56212     },
56213
56214     initEvents : function(){
56215         // Roo.form.Checkbox.superclass.initEvents.call(this);
56216         // has no events...
56217         
56218         if(this.closable){
56219             this.closeEl.on('click', this.onClose, this);
56220         }
56221        
56222     },
56223
56224
56225     getResizeEl : function(){
56226         return this.wrap;
56227     },
56228
56229     getPositionEl : function(){
56230         return this.wrap;
56231     },
56232
56233     // private
56234     onRender : function(ct, position){
56235         
56236         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56237         //if(this.inputValue !== undefined){
56238         this.wrap = this.el.wrap();
56239         
56240         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56241         
56242         if(this.closable){
56243             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56244         }
56245         
56246         if (this.bodyStyle) {
56247             this.viewEl.applyStyles(this.bodyStyle);
56248         }
56249         //this.viewEl.setStyle('padding', '2px');
56250         
56251         this.setValue(this.value);
56252         
56253     },
56254 /*
56255     // private
56256     initValue : Roo.emptyFn,
56257
56258   */
56259
56260         // private
56261     onClick : function(){
56262         
56263     },
56264
56265     /**
56266      * Sets the checked state of the checkbox.
56267      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56268      */
56269     setValue : function(v){
56270         this.value = v;
56271         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
56272         // this might be called before we have a dom element..
56273         if (!this.viewEl) {
56274             return;
56275         }
56276         this.viewEl.dom.innerHTML = html;
56277         Roo.form.DisplayField.superclass.setValue.call(this, v);
56278
56279     },
56280     
56281     onClose : function(e)
56282     {
56283         e.preventDefault();
56284         
56285         this.fireEvent('close', this);
56286     }
56287 });/*
56288  * 
56289  * Licence- LGPL
56290  * 
56291  */
56292
56293 /**
56294  * @class Roo.form.DayPicker
56295  * @extends Roo.form.Field
56296  * A Day picker show [M] [T] [W] ....
56297  * @constructor
56298  * Creates a new Day Picker
56299  * @param {Object} config Configuration options
56300  */
56301 Roo.form.DayPicker= function(config){
56302     Roo.form.DayPicker.superclass.constructor.call(this, config);
56303      
56304 };
56305
56306 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
56307     /**
56308      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56309      */
56310     focusClass : undefined,
56311     /**
56312      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56313      */
56314     fieldClass: "x-form-field",
56315    
56316     /**
56317      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56318      * {tag: "input", type: "checkbox", autocomplete: "off"})
56319      */
56320     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56321     
56322    
56323     actionMode : 'viewEl', 
56324     //
56325     // private
56326  
56327     inputType : 'hidden',
56328     
56329      
56330     inputElement: false, // real input element?
56331     basedOn: false, // ????
56332     
56333     isFormField: true, // not sure where this is needed!!!!
56334
56335     onResize : function(){
56336         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56337         if(!this.boxLabel){
56338             this.el.alignTo(this.wrap, 'c-c');
56339         }
56340     },
56341
56342     initEvents : function(){
56343         Roo.form.Checkbox.superclass.initEvents.call(this);
56344         this.el.on("click", this.onClick,  this);
56345         this.el.on("change", this.onClick,  this);
56346     },
56347
56348
56349     getResizeEl : function(){
56350         return this.wrap;
56351     },
56352
56353     getPositionEl : function(){
56354         return this.wrap;
56355     },
56356
56357     
56358     // private
56359     onRender : function(ct, position){
56360         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56361        
56362         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56363         
56364         var r1 = '<table><tr>';
56365         var r2 = '<tr class="x-form-daypick-icons">';
56366         for (var i=0; i < 7; i++) {
56367             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56368             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
56369         }
56370         
56371         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56372         viewEl.select('img').on('click', this.onClick, this);
56373         this.viewEl = viewEl;   
56374         
56375         
56376         // this will not work on Chrome!!!
56377         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
56378         this.el.on('propertychange', this.setFromHidden,  this);  //ie
56379         
56380         
56381           
56382
56383     },
56384
56385     // private
56386     initValue : Roo.emptyFn,
56387
56388     /**
56389      * Returns the checked state of the checkbox.
56390      * @return {Boolean} True if checked, else false
56391      */
56392     getValue : function(){
56393         return this.el.dom.value;
56394         
56395     },
56396
56397         // private
56398     onClick : function(e){ 
56399         //this.setChecked(!this.checked);
56400         Roo.get(e.target).toggleClass('x-menu-item-checked');
56401         this.refreshValue();
56402         //if(this.el.dom.checked != this.checked){
56403         //    this.setValue(this.el.dom.checked);
56404        // }
56405     },
56406     
56407     // private
56408     refreshValue : function()
56409     {
56410         var val = '';
56411         this.viewEl.select('img',true).each(function(e,i,n)  {
56412             val += e.is(".x-menu-item-checked") ? String(n) : '';
56413         });
56414         this.setValue(val, true);
56415     },
56416
56417     /**
56418      * Sets the checked state of the checkbox.
56419      * On is always based on a string comparison between inputValue and the param.
56420      * @param {Boolean/String} value - the value to set 
56421      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56422      */
56423     setValue : function(v,suppressEvent){
56424         if (!this.el.dom) {
56425             return;
56426         }
56427         var old = this.el.dom.value ;
56428         this.el.dom.value = v;
56429         if (suppressEvent) {
56430             return ;
56431         }
56432          
56433         // update display..
56434         this.viewEl.select('img',true).each(function(e,i,n)  {
56435             
56436             var on = e.is(".x-menu-item-checked");
56437             var newv = v.indexOf(String(n)) > -1;
56438             if (on != newv) {
56439                 e.toggleClass('x-menu-item-checked');
56440             }
56441             
56442         });
56443         
56444         
56445         this.fireEvent('change', this, v, old);
56446         
56447         
56448     },
56449    
56450     // handle setting of hidden value by some other method!!?!?
56451     setFromHidden: function()
56452     {
56453         if(!this.el){
56454             return;
56455         }
56456         //console.log("SET FROM HIDDEN");
56457         //alert('setFrom hidden');
56458         this.setValue(this.el.dom.value);
56459     },
56460     
56461     onDestroy : function()
56462     {
56463         if(this.viewEl){
56464             Roo.get(this.viewEl).remove();
56465         }
56466          
56467         Roo.form.DayPicker.superclass.onDestroy.call(this);
56468     }
56469
56470 });/*
56471  * RooJS Library 1.1.1
56472  * Copyright(c) 2008-2011  Alan Knowles
56473  *
56474  * License - LGPL
56475  */
56476  
56477
56478 /**
56479  * @class Roo.form.ComboCheck
56480  * @extends Roo.form.ComboBox
56481  * A combobox for multiple select items.
56482  *
56483  * FIXME - could do with a reset button..
56484  * 
56485  * @constructor
56486  * Create a new ComboCheck
56487  * @param {Object} config Configuration options
56488  */
56489 Roo.form.ComboCheck = function(config){
56490     Roo.form.ComboCheck.superclass.constructor.call(this, config);
56491     // should verify some data...
56492     // like
56493     // hiddenName = required..
56494     // displayField = required
56495     // valudField == required
56496     var req= [ 'hiddenName', 'displayField', 'valueField' ];
56497     var _t = this;
56498     Roo.each(req, function(e) {
56499         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56500             throw "Roo.form.ComboCheck : missing value for: " + e;
56501         }
56502     });
56503     
56504     
56505 };
56506
56507 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56508      
56509      
56510     editable : false,
56511      
56512     selectedClass: 'x-menu-item-checked', 
56513     
56514     // private
56515     onRender : function(ct, position){
56516         var _t = this;
56517         
56518         
56519         
56520         if(!this.tpl){
56521             var cls = 'x-combo-list';
56522
56523             
56524             this.tpl =  new Roo.Template({
56525                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
56526                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
56527                    '<span>{' + this.displayField + '}</span>' +
56528                     '</div>' 
56529                 
56530             });
56531         }
56532  
56533         
56534         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56535         this.view.singleSelect = false;
56536         this.view.multiSelect = true;
56537         this.view.toggleSelect = true;
56538         this.pageTb.add(new Roo.Toolbar.Fill(), {
56539             
56540             text: 'Done',
56541             handler: function()
56542             {
56543                 _t.collapse();
56544             }
56545         });
56546     },
56547     
56548     onViewOver : function(e, t){
56549         // do nothing...
56550         return;
56551         
56552     },
56553     
56554     onViewClick : function(doFocus,index){
56555         return;
56556         
56557     },
56558     select: function () {
56559         //Roo.log("SELECT CALLED");
56560     },
56561      
56562     selectByValue : function(xv, scrollIntoView){
56563         var ar = this.getValueArray();
56564         var sels = [];
56565         
56566         Roo.each(ar, function(v) {
56567             if(v === undefined || v === null){
56568                 return;
56569             }
56570             var r = this.findRecord(this.valueField, v);
56571             if(r){
56572                 sels.push(this.store.indexOf(r))
56573                 
56574             }
56575         },this);
56576         this.view.select(sels);
56577         return false;
56578     },
56579     
56580     
56581     
56582     onSelect : function(record, index){
56583        // Roo.log("onselect Called");
56584        // this is only called by the clear button now..
56585         this.view.clearSelections();
56586         this.setValue('[]');
56587         if (this.value != this.valueBefore) {
56588             this.fireEvent('change', this, this.value, this.valueBefore);
56589             this.valueBefore = this.value;
56590         }
56591     },
56592     getValueArray : function()
56593     {
56594         var ar = [] ;
56595         
56596         try {
56597             //Roo.log(this.value);
56598             if (typeof(this.value) == 'undefined') {
56599                 return [];
56600             }
56601             var ar = Roo.decode(this.value);
56602             return  ar instanceof Array ? ar : []; //?? valid?
56603             
56604         } catch(e) {
56605             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
56606             return [];
56607         }
56608          
56609     },
56610     expand : function ()
56611     {
56612         
56613         Roo.form.ComboCheck.superclass.expand.call(this);
56614         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56615         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56616         
56617
56618     },
56619     
56620     collapse : function(){
56621         Roo.form.ComboCheck.superclass.collapse.call(this);
56622         var sl = this.view.getSelectedIndexes();
56623         var st = this.store;
56624         var nv = [];
56625         var tv = [];
56626         var r;
56627         Roo.each(sl, function(i) {
56628             r = st.getAt(i);
56629             nv.push(r.get(this.valueField));
56630         },this);
56631         this.setValue(Roo.encode(nv));
56632         if (this.value != this.valueBefore) {
56633
56634             this.fireEvent('change', this, this.value, this.valueBefore);
56635             this.valueBefore = this.value;
56636         }
56637         
56638     },
56639     
56640     setValue : function(v){
56641         // Roo.log(v);
56642         this.value = v;
56643         
56644         var vals = this.getValueArray();
56645         var tv = [];
56646         Roo.each(vals, function(k) {
56647             var r = this.findRecord(this.valueField, k);
56648             if(r){
56649                 tv.push(r.data[this.displayField]);
56650             }else if(this.valueNotFoundText !== undefined){
56651                 tv.push( this.valueNotFoundText );
56652             }
56653         },this);
56654        // Roo.log(tv);
56655         
56656         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56657         this.hiddenField.value = v;
56658         this.value = v;
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  * @class Roo.form.Signature
56674  * @extends Roo.form.Field
56675  * Signature field.  
56676  * @constructor
56677  * 
56678  * @param {Object} config Configuration options
56679  */
56680
56681 Roo.form.Signature = function(config){
56682     Roo.form.Signature.superclass.constructor.call(this, config);
56683     
56684     this.addEvents({// not in used??
56685          /**
56686          * @event confirm
56687          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56688              * @param {Roo.form.Signature} combo This combo box
56689              */
56690         'confirm' : true,
56691         /**
56692          * @event reset
56693          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56694              * @param {Roo.form.ComboBox} combo This combo box
56695              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56696              */
56697         'reset' : true
56698     });
56699 };
56700
56701 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
56702     /**
56703      * @cfg {Object} labels Label to use when rendering a form.
56704      * defaults to 
56705      * labels : { 
56706      *      clear : "Clear",
56707      *      confirm : "Confirm"
56708      *  }
56709      */
56710     labels : { 
56711         clear : "Clear",
56712         confirm : "Confirm"
56713     },
56714     /**
56715      * @cfg {Number} width The signature panel width (defaults to 300)
56716      */
56717     width: 300,
56718     /**
56719      * @cfg {Number} height The signature panel height (defaults to 100)
56720      */
56721     height : 100,
56722     /**
56723      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56724      */
56725     allowBlank : false,
56726     
56727     //private
56728     // {Object} signPanel The signature SVG panel element (defaults to {})
56729     signPanel : {},
56730     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56731     isMouseDown : false,
56732     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56733     isConfirmed : false,
56734     // {String} signatureTmp SVG mapping string (defaults to empty string)
56735     signatureTmp : '',
56736     
56737     
56738     defaultAutoCreate : { // modified by initCompnoent..
56739         tag: "input",
56740         type:"hidden"
56741     },
56742
56743     // private
56744     onRender : function(ct, position){
56745         
56746         Roo.form.Signature.superclass.onRender.call(this, ct, position);
56747         
56748         this.wrap = this.el.wrap({
56749             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
56750         });
56751         
56752         this.createToolbar(this);
56753         this.signPanel = this.wrap.createChild({
56754                 tag: 'div',
56755                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
56756             }, this.el
56757         );
56758             
56759         this.svgID = Roo.id();
56760         this.svgEl = this.signPanel.createChild({
56761               xmlns : 'http://www.w3.org/2000/svg',
56762               tag : 'svg',
56763               id : this.svgID + "-svg",
56764               width: this.width,
56765               height: this.height,
56766               viewBox: '0 0 '+this.width+' '+this.height,
56767               cn : [
56768                 {
56769                     tag: "rect",
56770                     id: this.svgID + "-svg-r",
56771                     width: this.width,
56772                     height: this.height,
56773                     fill: "#ffa"
56774                 },
56775                 {
56776                     tag: "line",
56777                     id: this.svgID + "-svg-l",
56778                     x1: "0", // start
56779                     y1: (this.height*0.8), // start set the line in 80% of height
56780                     x2: this.width, // end
56781                     y2: (this.height*0.8), // end set the line in 80% of height
56782                     'stroke': "#666",
56783                     'stroke-width': "1",
56784                     'stroke-dasharray': "3",
56785                     'shape-rendering': "crispEdges",
56786                     'pointer-events': "none"
56787                 },
56788                 {
56789                     tag: "path",
56790                     id: this.svgID + "-svg-p",
56791                     'stroke': "navy",
56792                     'stroke-width': "3",
56793                     'fill': "none",
56794                     'pointer-events': 'none'
56795                 }
56796               ]
56797         });
56798         this.createSVG();
56799         this.svgBox = this.svgEl.dom.getScreenCTM();
56800     },
56801     createSVG : function(){ 
56802         var svg = this.signPanel;
56803         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
56804         var t = this;
56805
56806         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
56807         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
56808         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
56809         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
56810         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
56811         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
56812         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
56813         
56814     },
56815     isTouchEvent : function(e){
56816         return e.type.match(/^touch/);
56817     },
56818     getCoords : function (e) {
56819         var pt    = this.svgEl.dom.createSVGPoint();
56820         pt.x = e.clientX; 
56821         pt.y = e.clientY;
56822         if (this.isTouchEvent(e)) {
56823             pt.x =  e.targetTouches[0].clientX;
56824             pt.y = e.targetTouches[0].clientY;
56825         }
56826         var a = this.svgEl.dom.getScreenCTM();
56827         var b = a.inverse();
56828         var mx = pt.matrixTransform(b);
56829         return mx.x + ',' + mx.y;
56830     },
56831     //mouse event headler 
56832     down : function (e) {
56833         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
56834         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
56835         
56836         this.isMouseDown = true;
56837         
56838         e.preventDefault();
56839     },
56840     move : function (e) {
56841         if (this.isMouseDown) {
56842             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
56843             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
56844         }
56845         
56846         e.preventDefault();
56847     },
56848     up : function (e) {
56849         this.isMouseDown = false;
56850         var sp = this.signatureTmp.split(' ');
56851         
56852         if(sp.length > 1){
56853             if(!sp[sp.length-2].match(/^L/)){
56854                 sp.pop();
56855                 sp.pop();
56856                 sp.push("");
56857                 this.signatureTmp = sp.join(" ");
56858             }
56859         }
56860         if(this.getValue() != this.signatureTmp){
56861             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56862             this.isConfirmed = false;
56863         }
56864         e.preventDefault();
56865     },
56866     
56867     /**
56868      * Protected method that will not generally be called directly. It
56869      * is called when the editor creates its toolbar. Override this method if you need to
56870      * add custom toolbar buttons.
56871      * @param {HtmlEditor} editor
56872      */
56873     createToolbar : function(editor){
56874          function btn(id, toggle, handler){
56875             var xid = fid + '-'+ id ;
56876             return {
56877                 id : xid,
56878                 cmd : id,
56879                 cls : 'x-btn-icon x-edit-'+id,
56880                 enableToggle:toggle !== false,
56881                 scope: editor, // was editor...
56882                 handler:handler||editor.relayBtnCmd,
56883                 clickEvent:'mousedown',
56884                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
56885                 tabIndex:-1
56886             };
56887         }
56888         
56889         
56890         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
56891         this.tb = tb;
56892         this.tb.add(
56893            {
56894                 cls : ' x-signature-btn x-signature-'+id,
56895                 scope: editor, // was editor...
56896                 handler: this.reset,
56897                 clickEvent:'mousedown',
56898                 text: this.labels.clear
56899             },
56900             {
56901                  xtype : 'Fill',
56902                  xns: Roo.Toolbar
56903             }, 
56904             {
56905                 cls : '  x-signature-btn x-signature-'+id,
56906                 scope: editor, // was editor...
56907                 handler: this.confirmHandler,
56908                 clickEvent:'mousedown',
56909                 text: this.labels.confirm
56910             }
56911         );
56912     
56913     },
56914     //public
56915     /**
56916      * when user is clicked confirm then show this image.....
56917      * 
56918      * @return {String} Image Data URI
56919      */
56920     getImageDataURI : function(){
56921         var svg = this.svgEl.dom.parentNode.innerHTML;
56922         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
56923         return src; 
56924     },
56925     /**
56926      * 
56927      * @return {Boolean} this.isConfirmed
56928      */
56929     getConfirmed : function(){
56930         return this.isConfirmed;
56931     },
56932     /**
56933      * 
56934      * @return {Number} this.width
56935      */
56936     getWidth : function(){
56937         return this.width;
56938     },
56939     /**
56940      * 
56941      * @return {Number} this.height
56942      */
56943     getHeight : function(){
56944         return this.height;
56945     },
56946     // private
56947     getSignature : function(){
56948         return this.signatureTmp;
56949     },
56950     // private
56951     reset : function(){
56952         this.signatureTmp = '';
56953         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56954         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
56955         this.isConfirmed = false;
56956         Roo.form.Signature.superclass.reset.call(this);
56957     },
56958     setSignature : function(s){
56959         this.signatureTmp = s;
56960         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56961         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
56962         this.setValue(s);
56963         this.isConfirmed = false;
56964         Roo.form.Signature.superclass.reset.call(this);
56965     }, 
56966     test : function(){
56967 //        Roo.log(this.signPanel.dom.contentWindow.up())
56968     },
56969     //private
56970     setConfirmed : function(){
56971         
56972         
56973         
56974 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
56975     },
56976     // private
56977     confirmHandler : function(){
56978         if(!this.getSignature()){
56979             return;
56980         }
56981         
56982         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
56983         this.setValue(this.getSignature());
56984         this.isConfirmed = true;
56985         
56986         this.fireEvent('confirm', this);
56987     },
56988     // private
56989     // Subclasses should provide the validation implementation by overriding this
56990     validateValue : function(value){
56991         if(this.allowBlank){
56992             return true;
56993         }
56994         
56995         if(this.isConfirmed){
56996             return true;
56997         }
56998         return false;
56999     }
57000 });/*
57001  * Based on:
57002  * Ext JS Library 1.1.1
57003  * Copyright(c) 2006-2007, Ext JS, LLC.
57004  *
57005  * Originally Released Under LGPL - original licence link has changed is not relivant.
57006  *
57007  * Fork - LGPL
57008  * <script type="text/javascript">
57009  */
57010  
57011
57012 /**
57013  * @class Roo.form.ComboBox
57014  * @extends Roo.form.TriggerField
57015  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57016  * @constructor
57017  * Create a new ComboBox.
57018  * @param {Object} config Configuration options
57019  */
57020 Roo.form.Select = function(config){
57021     Roo.form.Select.superclass.constructor.call(this, config);
57022      
57023 };
57024
57025 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57026     /**
57027      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57028      */
57029     /**
57030      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57031      * rendering into an Roo.Editor, defaults to false)
57032      */
57033     /**
57034      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57035      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57036      */
57037     /**
57038      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57039      */
57040     /**
57041      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57042      * the dropdown list (defaults to undefined, with no header element)
57043      */
57044
57045      /**
57046      * @cfg {String/Roo.Template} tpl The template to use to render the output
57047      */
57048      
57049     // private
57050     defaultAutoCreate : {tag: "select"  },
57051     /**
57052      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57053      */
57054     listWidth: undefined,
57055     /**
57056      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57057      * mode = 'remote' or 'text' if mode = 'local')
57058      */
57059     displayField: undefined,
57060     /**
57061      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57062      * mode = 'remote' or 'value' if mode = 'local'). 
57063      * Note: use of a valueField requires the user make a selection
57064      * in order for a value to be mapped.
57065      */
57066     valueField: undefined,
57067     
57068     
57069     /**
57070      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57071      * field's data value (defaults to the underlying DOM element's name)
57072      */
57073     hiddenName: undefined,
57074     /**
57075      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57076      */
57077     listClass: '',
57078     /**
57079      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57080      */
57081     selectedClass: 'x-combo-selected',
57082     /**
57083      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
57084      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57085      * which displays a downward arrow icon).
57086      */
57087     triggerClass : 'x-form-arrow-trigger',
57088     /**
57089      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57090      */
57091     shadow:'sides',
57092     /**
57093      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57094      * anchor positions (defaults to 'tl-bl')
57095      */
57096     listAlign: 'tl-bl?',
57097     /**
57098      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57099      */
57100     maxHeight: 300,
57101     /**
57102      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
57103      * query specified by the allQuery config option (defaults to 'query')
57104      */
57105     triggerAction: 'query',
57106     /**
57107      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57108      * (defaults to 4, does not apply if editable = false)
57109      */
57110     minChars : 4,
57111     /**
57112      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57113      * delay (typeAheadDelay) if it matches a known value (defaults to false)
57114      */
57115     typeAhead: false,
57116     /**
57117      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57118      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57119      */
57120     queryDelay: 500,
57121     /**
57122      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57123      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
57124      */
57125     pageSize: 0,
57126     /**
57127      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
57128      * when editable = true (defaults to false)
57129      */
57130     selectOnFocus:false,
57131     /**
57132      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57133      */
57134     queryParam: 'query',
57135     /**
57136      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
57137      * when mode = 'remote' (defaults to 'Loading...')
57138      */
57139     loadingText: 'Loading...',
57140     /**
57141      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57142      */
57143     resizable: false,
57144     /**
57145      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57146      */
57147     handleHeight : 8,
57148     /**
57149      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57150      * traditional select (defaults to true)
57151      */
57152     editable: true,
57153     /**
57154      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57155      */
57156     allQuery: '',
57157     /**
57158      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57159      */
57160     mode: 'remote',
57161     /**
57162      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57163      * listWidth has a higher value)
57164      */
57165     minListWidth : 70,
57166     /**
57167      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57168      * allow the user to set arbitrary text into the field (defaults to false)
57169      */
57170     forceSelection:false,
57171     /**
57172      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57173      * if typeAhead = true (defaults to 250)
57174      */
57175     typeAheadDelay : 250,
57176     /**
57177      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57178      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57179      */
57180     valueNotFoundText : undefined,
57181     
57182     /**
57183      * @cfg {String} defaultValue The value displayed after loading the store.
57184      */
57185     defaultValue: '',
57186     
57187     /**
57188      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57189      */
57190     blockFocus : false,
57191     
57192     /**
57193      * @cfg {Boolean} disableClear Disable showing of clear button.
57194      */
57195     disableClear : false,
57196     /**
57197      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
57198      */
57199     alwaysQuery : false,
57200     
57201     //private
57202     addicon : false,
57203     editicon: false,
57204     
57205     // element that contains real text value.. (when hidden is used..)
57206      
57207     // private
57208     onRender : function(ct, position){
57209         Roo.form.Field.prototype.onRender.call(this, ct, position);
57210         
57211         if(this.store){
57212             this.store.on('beforeload', this.onBeforeLoad, this);
57213             this.store.on('load', this.onLoad, this);
57214             this.store.on('loadexception', this.onLoadException, this);
57215             this.store.load({});
57216         }
57217         
57218         
57219         
57220     },
57221
57222     // private
57223     initEvents : function(){
57224         //Roo.form.ComboBox.superclass.initEvents.call(this);
57225  
57226     },
57227
57228     onDestroy : function(){
57229        
57230         if(this.store){
57231             this.store.un('beforeload', this.onBeforeLoad, this);
57232             this.store.un('load', this.onLoad, this);
57233             this.store.un('loadexception', this.onLoadException, this);
57234         }
57235         //Roo.form.ComboBox.superclass.onDestroy.call(this);
57236     },
57237
57238     // private
57239     fireKey : function(e){
57240         if(e.isNavKeyPress() && !this.list.isVisible()){
57241             this.fireEvent("specialkey", this, e);
57242         }
57243     },
57244
57245     // private
57246     onResize: function(w, h){
57247         
57248         return; 
57249     
57250         
57251     },
57252
57253     /**
57254      * Allow or prevent the user from directly editing the field text.  If false is passed,
57255      * the user will only be able to select from the items defined in the dropdown list.  This method
57256      * is the runtime equivalent of setting the 'editable' config option at config time.
57257      * @param {Boolean} value True to allow the user to directly edit the field text
57258      */
57259     setEditable : function(value){
57260          
57261     },
57262
57263     // private
57264     onBeforeLoad : function(){
57265         
57266         Roo.log("Select before load");
57267         return;
57268     
57269         this.innerList.update(this.loadingText ?
57270                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57271         //this.restrictHeight();
57272         this.selectedIndex = -1;
57273     },
57274
57275     // private
57276     onLoad : function(){
57277
57278     
57279         var dom = this.el.dom;
57280         dom.innerHTML = '';
57281          var od = dom.ownerDocument;
57282          
57283         if (this.emptyText) {
57284             var op = od.createElement('option');
57285             op.setAttribute('value', '');
57286             op.innerHTML = String.format('{0}', this.emptyText);
57287             dom.appendChild(op);
57288         }
57289         if(this.store.getCount() > 0){
57290            
57291             var vf = this.valueField;
57292             var df = this.displayField;
57293             this.store.data.each(function(r) {
57294                 // which colmsn to use... testing - cdoe / title..
57295                 var op = od.createElement('option');
57296                 op.setAttribute('value', r.data[vf]);
57297                 op.innerHTML = String.format('{0}', r.data[df]);
57298                 dom.appendChild(op);
57299             });
57300             if (typeof(this.defaultValue != 'undefined')) {
57301                 this.setValue(this.defaultValue);
57302             }
57303             
57304              
57305         }else{
57306             //this.onEmptyResults();
57307         }
57308         //this.el.focus();
57309     },
57310     // private
57311     onLoadException : function()
57312     {
57313         dom.innerHTML = '';
57314             
57315         Roo.log("Select on load exception");
57316         return;
57317     
57318         this.collapse();
57319         Roo.log(this.store.reader.jsonData);
57320         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57321             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57322         }
57323         
57324         
57325     },
57326     // private
57327     onTypeAhead : function(){
57328          
57329     },
57330
57331     // private
57332     onSelect : function(record, index){
57333         Roo.log('on select?');
57334         return;
57335         if(this.fireEvent('beforeselect', this, record, index) !== false){
57336             this.setFromData(index > -1 ? record.data : false);
57337             this.collapse();
57338             this.fireEvent('select', this, record, index);
57339         }
57340     },
57341
57342     /**
57343      * Returns the currently selected field value or empty string if no value is set.
57344      * @return {String} value The selected value
57345      */
57346     getValue : function(){
57347         var dom = this.el.dom;
57348         this.value = dom.options[dom.selectedIndex].value;
57349         return this.value;
57350         
57351     },
57352
57353     /**
57354      * Clears any text/value currently set in the field
57355      */
57356     clearValue : function(){
57357         this.value = '';
57358         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57359         
57360     },
57361
57362     /**
57363      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
57364      * will be displayed in the field.  If the value does not match the data value of an existing item,
57365      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57366      * Otherwise the field will be blank (although the value will still be set).
57367      * @param {String} value The value to match
57368      */
57369     setValue : function(v){
57370         var d = this.el.dom;
57371         for (var i =0; i < d.options.length;i++) {
57372             if (v == d.options[i].value) {
57373                 d.selectedIndex = i;
57374                 this.value = v;
57375                 return;
57376             }
57377         }
57378         this.clearValue();
57379     },
57380     /**
57381      * @property {Object} the last set data for the element
57382      */
57383     
57384     lastData : false,
57385     /**
57386      * Sets the value of the field based on a object which is related to the record format for the store.
57387      * @param {Object} value the value to set as. or false on reset?
57388      */
57389     setFromData : function(o){
57390         Roo.log('setfrom data?');
57391          
57392         
57393         
57394     },
57395     // private
57396     reset : function(){
57397         this.clearValue();
57398     },
57399     // private
57400     findRecord : function(prop, value){
57401         
57402         return false;
57403     
57404         var record;
57405         if(this.store.getCount() > 0){
57406             this.store.each(function(r){
57407                 if(r.data[prop] == value){
57408                     record = r;
57409                     return false;
57410                 }
57411                 return true;
57412             });
57413         }
57414         return record;
57415     },
57416     
57417     getName: function()
57418     {
57419         // returns hidden if it's set..
57420         if (!this.rendered) {return ''};
57421         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
57422         
57423     },
57424      
57425
57426     
57427
57428     // private
57429     onEmptyResults : function(){
57430         Roo.log('empty results');
57431         //this.collapse();
57432     },
57433
57434     /**
57435      * Returns true if the dropdown list is expanded, else false.
57436      */
57437     isExpanded : function(){
57438         return false;
57439     },
57440
57441     /**
57442      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57443      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57444      * @param {String} value The data value of the item to select
57445      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57446      * selected item if it is not currently in view (defaults to true)
57447      * @return {Boolean} True if the value matched an item in the list, else false
57448      */
57449     selectByValue : function(v, scrollIntoView){
57450         Roo.log('select By Value');
57451         return false;
57452     
57453         if(v !== undefined && v !== null){
57454             var r = this.findRecord(this.valueField || this.displayField, v);
57455             if(r){
57456                 this.select(this.store.indexOf(r), scrollIntoView);
57457                 return true;
57458             }
57459         }
57460         return false;
57461     },
57462
57463     /**
57464      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57465      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57466      * @param {Number} index The zero-based index of the list item to select
57467      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57468      * selected item if it is not currently in view (defaults to true)
57469      */
57470     select : function(index, scrollIntoView){
57471         Roo.log('select ');
57472         return  ;
57473         
57474         this.selectedIndex = index;
57475         this.view.select(index);
57476         if(scrollIntoView !== false){
57477             var el = this.view.getNode(index);
57478             if(el){
57479                 this.innerList.scrollChildIntoView(el, false);
57480             }
57481         }
57482     },
57483
57484       
57485
57486     // private
57487     validateBlur : function(){
57488         
57489         return;
57490         
57491     },
57492
57493     // private
57494     initQuery : function(){
57495         this.doQuery(this.getRawValue());
57496     },
57497
57498     // private
57499     doForce : function(){
57500         if(this.el.dom.value.length > 0){
57501             this.el.dom.value =
57502                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57503              
57504         }
57505     },
57506
57507     /**
57508      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
57509      * query allowing the query action to be canceled if needed.
57510      * @param {String} query The SQL query to execute
57511      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57512      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
57513      * saved in the current store (defaults to false)
57514      */
57515     doQuery : function(q, forceAll){
57516         
57517         Roo.log('doQuery?');
57518         if(q === undefined || q === null){
57519             q = '';
57520         }
57521         var qe = {
57522             query: q,
57523             forceAll: forceAll,
57524             combo: this,
57525             cancel:false
57526         };
57527         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57528             return false;
57529         }
57530         q = qe.query;
57531         forceAll = qe.forceAll;
57532         if(forceAll === true || (q.length >= this.minChars)){
57533             if(this.lastQuery != q || this.alwaysQuery){
57534                 this.lastQuery = q;
57535                 if(this.mode == 'local'){
57536                     this.selectedIndex = -1;
57537                     if(forceAll){
57538                         this.store.clearFilter();
57539                     }else{
57540                         this.store.filter(this.displayField, q);
57541                     }
57542                     this.onLoad();
57543                 }else{
57544                     this.store.baseParams[this.queryParam] = q;
57545                     this.store.load({
57546                         params: this.getParams(q)
57547                     });
57548                     this.expand();
57549                 }
57550             }else{
57551                 this.selectedIndex = -1;
57552                 this.onLoad();   
57553             }
57554         }
57555     },
57556
57557     // private
57558     getParams : function(q){
57559         var p = {};
57560         //p[this.queryParam] = q;
57561         if(this.pageSize){
57562             p.start = 0;
57563             p.limit = this.pageSize;
57564         }
57565         return p;
57566     },
57567
57568     /**
57569      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57570      */
57571     collapse : function(){
57572         
57573     },
57574
57575     // private
57576     collapseIf : function(e){
57577         
57578     },
57579
57580     /**
57581      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57582      */
57583     expand : function(){
57584         
57585     } ,
57586
57587     // private
57588      
57589
57590     /** 
57591     * @cfg {Boolean} grow 
57592     * @hide 
57593     */
57594     /** 
57595     * @cfg {Number} growMin 
57596     * @hide 
57597     */
57598     /** 
57599     * @cfg {Number} growMax 
57600     * @hide 
57601     */
57602     /**
57603      * @hide
57604      * @method autoSize
57605      */
57606     
57607     setWidth : function()
57608     {
57609         
57610     },
57611     getResizeEl : function(){
57612         return this.el;
57613     }
57614 });//<script type="text/javasscript">
57615  
57616
57617 /**
57618  * @class Roo.DDView
57619  * A DnD enabled version of Roo.View.
57620  * @param {Element/String} container The Element in which to create the View.
57621  * @param {String} tpl The template string used to create the markup for each element of the View
57622  * @param {Object} config The configuration properties. These include all the config options of
57623  * {@link Roo.View} plus some specific to this class.<br>
57624  * <p>
57625  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57626  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57627  * <p>
57628  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57629 .x-view-drag-insert-above {
57630         border-top:1px dotted #3366cc;
57631 }
57632 .x-view-drag-insert-below {
57633         border-bottom:1px dotted #3366cc;
57634 }
57635 </code></pre>
57636  * 
57637  */
57638  
57639 Roo.DDView = function(container, tpl, config) {
57640     Roo.DDView.superclass.constructor.apply(this, arguments);
57641     this.getEl().setStyle("outline", "0px none");
57642     this.getEl().unselectable();
57643     if (this.dragGroup) {
57644         this.setDraggable(this.dragGroup.split(","));
57645     }
57646     if (this.dropGroup) {
57647         this.setDroppable(this.dropGroup.split(","));
57648     }
57649     if (this.deletable) {
57650         this.setDeletable();
57651     }
57652     this.isDirtyFlag = false;
57653         this.addEvents({
57654                 "drop" : true
57655         });
57656 };
57657
57658 Roo.extend(Roo.DDView, Roo.View, {
57659 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57660 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57661 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57662 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57663
57664         isFormField: true,
57665
57666         reset: Roo.emptyFn,
57667         
57668         clearInvalid: Roo.form.Field.prototype.clearInvalid,
57669
57670         validate: function() {
57671                 return true;
57672         },
57673         
57674         destroy: function() {
57675                 this.purgeListeners();
57676                 this.getEl.removeAllListeners();
57677                 this.getEl().remove();
57678                 if (this.dragZone) {
57679                         if (this.dragZone.destroy) {
57680                                 this.dragZone.destroy();
57681                         }
57682                 }
57683                 if (this.dropZone) {
57684                         if (this.dropZone.destroy) {
57685                                 this.dropZone.destroy();
57686                         }
57687                 }
57688         },
57689
57690 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57691         getName: function() {
57692                 return this.name;
57693         },
57694
57695 /**     Loads the View from a JSON string representing the Records to put into the Store. */
57696         setValue: function(v) {
57697                 if (!this.store) {
57698                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
57699                 }
57700                 var data = {};
57701                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57702                 this.store.proxy = new Roo.data.MemoryProxy(data);
57703                 this.store.load();
57704         },
57705
57706 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
57707         getValue: function() {
57708                 var result = '(';
57709                 this.store.each(function(rec) {
57710                         result += rec.id + ',';
57711                 });
57712                 return result.substr(0, result.length - 1) + ')';
57713         },
57714         
57715         getIds: function() {
57716                 var i = 0, result = new Array(this.store.getCount());
57717                 this.store.each(function(rec) {
57718                         result[i++] = rec.id;
57719                 });
57720                 return result;
57721         },
57722         
57723         isDirty: function() {
57724                 return this.isDirtyFlag;
57725         },
57726
57727 /**
57728  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
57729  *      whole Element becomes the target, and this causes the drop gesture to append.
57730  */
57731     getTargetFromEvent : function(e) {
57732                 var target = e.getTarget();
57733                 while ((target !== null) && (target.parentNode != this.el.dom)) {
57734                 target = target.parentNode;
57735                 }
57736                 if (!target) {
57737                         target = this.el.dom.lastChild || this.el.dom;
57738                 }
57739                 return target;
57740     },
57741
57742 /**
57743  *      Create the drag data which consists of an object which has the property "ddel" as
57744  *      the drag proxy element. 
57745  */
57746     getDragData : function(e) {
57747         var target = this.findItemFromChild(e.getTarget());
57748                 if(target) {
57749                         this.handleSelection(e);
57750                         var selNodes = this.getSelectedNodes();
57751             var dragData = {
57752                 source: this,
57753                 copy: this.copy || (this.allowCopy && e.ctrlKey),
57754                 nodes: selNodes,
57755                 records: []
57756                         };
57757                         var selectedIndices = this.getSelectedIndexes();
57758                         for (var i = 0; i < selectedIndices.length; i++) {
57759                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
57760                         }
57761                         if (selNodes.length == 1) {
57762                                 dragData.ddel = target.cloneNode(true); // the div element
57763                         } else {
57764                                 var div = document.createElement('div'); // create the multi element drag "ghost"
57765                                 div.className = 'multi-proxy';
57766                                 for (var i = 0, len = selNodes.length; i < len; i++) {
57767                                         div.appendChild(selNodes[i].cloneNode(true));
57768                                 }
57769                                 dragData.ddel = div;
57770                         }
57771             //console.log(dragData)
57772             //console.log(dragData.ddel.innerHTML)
57773                         return dragData;
57774                 }
57775         //console.log('nodragData')
57776                 return false;
57777     },
57778     
57779 /**     Specify to which ddGroup items in this DDView may be dragged. */
57780     setDraggable: function(ddGroup) {
57781         if (ddGroup instanceof Array) {
57782                 Roo.each(ddGroup, this.setDraggable, this);
57783                 return;
57784         }
57785         if (this.dragZone) {
57786                 this.dragZone.addToGroup(ddGroup);
57787         } else {
57788                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
57789                                 containerScroll: true,
57790                                 ddGroup: ddGroup 
57791
57792                         });
57793 //                      Draggability implies selection. DragZone's mousedown selects the element.
57794                         if (!this.multiSelect) { this.singleSelect = true; }
57795
57796 //                      Wire the DragZone's handlers up to methods in *this*
57797                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
57798                 }
57799     },
57800
57801 /**     Specify from which ddGroup this DDView accepts drops. */
57802     setDroppable: function(ddGroup) {
57803         if (ddGroup instanceof Array) {
57804                 Roo.each(ddGroup, this.setDroppable, this);
57805                 return;
57806         }
57807         if (this.dropZone) {
57808                 this.dropZone.addToGroup(ddGroup);
57809         } else {
57810                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
57811                                 containerScroll: true,
57812                                 ddGroup: ddGroup
57813                         });
57814
57815 //                      Wire the DropZone's handlers up to methods in *this*
57816                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
57817                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
57818                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
57819                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
57820                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
57821                 }
57822     },
57823
57824 /**     Decide whether to drop above or below a View node. */
57825     getDropPoint : function(e, n, dd){
57826         if (n == this.el.dom) { return "above"; }
57827                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
57828                 var c = t + (b - t) / 2;
57829                 var y = Roo.lib.Event.getPageY(e);
57830                 if(y <= c) {
57831                         return "above";
57832                 }else{
57833                         return "below";
57834                 }
57835     },
57836
57837     onNodeEnter : function(n, dd, e, data){
57838                 return false;
57839     },
57840     
57841     onNodeOver : function(n, dd, e, data){
57842                 var pt = this.getDropPoint(e, n, dd);
57843                 // set the insert point style on the target node
57844                 var dragElClass = this.dropNotAllowed;
57845                 if (pt) {
57846                         var targetElClass;
57847                         if (pt == "above"){
57848                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
57849                                 targetElClass = "x-view-drag-insert-above";
57850                         } else {
57851                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
57852                                 targetElClass = "x-view-drag-insert-below";
57853                         }
57854                         if (this.lastInsertClass != targetElClass){
57855                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
57856                                 this.lastInsertClass = targetElClass;
57857                         }
57858                 }
57859                 return dragElClass;
57860         },
57861
57862     onNodeOut : function(n, dd, e, data){
57863                 this.removeDropIndicators(n);
57864     },
57865
57866     onNodeDrop : function(n, dd, e, data){
57867         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
57868                 return false;
57869         }
57870         var pt = this.getDropPoint(e, n, dd);
57871                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
57872                 if (pt == "below") { insertAt++; }
57873                 for (var i = 0; i < data.records.length; i++) {
57874                         var r = data.records[i];
57875                         var dup = this.store.getById(r.id);
57876                         if (dup && (dd != this.dragZone)) {
57877                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
57878                         } else {
57879                                 if (data.copy) {
57880                                         this.store.insert(insertAt++, r.copy());
57881                                 } else {
57882                                         data.source.isDirtyFlag = true;
57883                                         r.store.remove(r);
57884                                         this.store.insert(insertAt++, r);
57885                                 }
57886                                 this.isDirtyFlag = true;
57887                         }
57888                 }
57889                 this.dragZone.cachedTarget = null;
57890                 return true;
57891     },
57892
57893     removeDropIndicators : function(n){
57894                 if(n){
57895                         Roo.fly(n).removeClass([
57896                                 "x-view-drag-insert-above",
57897                                 "x-view-drag-insert-below"]);
57898                         this.lastInsertClass = "_noclass";
57899                 }
57900     },
57901
57902 /**
57903  *      Utility method. Add a delete option to the DDView's context menu.
57904  *      @param {String} imageUrl The URL of the "delete" icon image.
57905  */
57906         setDeletable: function(imageUrl) {
57907                 if (!this.singleSelect && !this.multiSelect) {
57908                         this.singleSelect = true;
57909                 }
57910                 var c = this.getContextMenu();
57911                 this.contextMenu.on("itemclick", function(item) {
57912                         switch (item.id) {
57913                                 case "delete":
57914                                         this.remove(this.getSelectedIndexes());
57915                                         break;
57916                         }
57917                 }, this);
57918                 this.contextMenu.add({
57919                         icon: imageUrl,
57920                         id: "delete",
57921                         text: 'Delete'
57922                 });
57923         },
57924         
57925 /**     Return the context menu for this DDView. */
57926         getContextMenu: function() {
57927                 if (!this.contextMenu) {
57928 //                      Create the View's context menu
57929                         this.contextMenu = new Roo.menu.Menu({
57930                                 id: this.id + "-contextmenu"
57931                         });
57932                         this.el.on("contextmenu", this.showContextMenu, this);
57933                 }
57934                 return this.contextMenu;
57935         },
57936         
57937         disableContextMenu: function() {
57938                 if (this.contextMenu) {
57939                         this.el.un("contextmenu", this.showContextMenu, this);
57940                 }
57941         },
57942
57943         showContextMenu: function(e, item) {
57944         item = this.findItemFromChild(e.getTarget());
57945                 if (item) {
57946                         e.stopEvent();
57947                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
57948                         this.contextMenu.showAt(e.getXY());
57949             }
57950     },
57951
57952 /**
57953  *      Remove {@link Roo.data.Record}s at the specified indices.
57954  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
57955  */
57956     remove: function(selectedIndices) {
57957                 selectedIndices = [].concat(selectedIndices);
57958                 for (var i = 0; i < selectedIndices.length; i++) {
57959                         var rec = this.store.getAt(selectedIndices[i]);
57960                         this.store.remove(rec);
57961                 }
57962     },
57963
57964 /**
57965  *      Double click fires the event, but also, if this is draggable, and there is only one other
57966  *      related DropZone, it transfers the selected node.
57967  */
57968     onDblClick : function(e){
57969         var item = this.findItemFromChild(e.getTarget());
57970         if(item){
57971             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
57972                 return false;
57973             }
57974             if (this.dragGroup) {
57975                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
57976                     while (targets.indexOf(this.dropZone) > -1) {
57977                             targets.remove(this.dropZone);
57978                                 }
57979                     if (targets.length == 1) {
57980                                         this.dragZone.cachedTarget = null;
57981                         var el = Roo.get(targets[0].getEl());
57982                         var box = el.getBox(true);
57983                         targets[0].onNodeDrop(el.dom, {
57984                                 target: el.dom,
57985                                 xy: [box.x, box.y + box.height - 1]
57986                         }, null, this.getDragData(e));
57987                     }
57988                 }
57989         }
57990     },
57991     
57992     handleSelection: function(e) {
57993                 this.dragZone.cachedTarget = null;
57994         var item = this.findItemFromChild(e.getTarget());
57995         if (!item) {
57996                 this.clearSelections(true);
57997                 return;
57998         }
57999                 if (item && (this.multiSelect || this.singleSelect)){
58000                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
58001                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58002                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58003                                 this.unselect(item);
58004                         } else {
58005                                 this.select(item, this.multiSelect && e.ctrlKey);
58006                                 this.lastSelection = item;
58007                         }
58008                 }
58009     },
58010
58011     onItemClick : function(item, index, e){
58012                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58013                         return false;
58014                 }
58015                 return true;
58016     },
58017
58018     unselect : function(nodeInfo, suppressEvent){
58019                 var node = this.getNode(nodeInfo);
58020                 if(node && this.isSelected(node)){
58021                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58022                                 Roo.fly(node).removeClass(this.selectedClass);
58023                                 this.selections.remove(node);
58024                                 if(!suppressEvent){
58025                                         this.fireEvent("selectionchange", this, this.selections);
58026                                 }
58027                         }
58028                 }
58029     }
58030 });
58031 /*
58032  * Based on:
58033  * Ext JS Library 1.1.1
58034  * Copyright(c) 2006-2007, Ext JS, LLC.
58035  *
58036  * Originally Released Under LGPL - original licence link has changed is not relivant.
58037  *
58038  * Fork - LGPL
58039  * <script type="text/javascript">
58040  */
58041  
58042 /**
58043  * @class Roo.LayoutManager
58044  * @extends Roo.util.Observable
58045  * Base class for layout managers.
58046  */
58047 Roo.LayoutManager = function(container, config){
58048     Roo.LayoutManager.superclass.constructor.call(this);
58049     this.el = Roo.get(container);
58050     // ie scrollbar fix
58051     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58052         document.body.scroll = "no";
58053     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58054         this.el.position('relative');
58055     }
58056     this.id = this.el.id;
58057     this.el.addClass("x-layout-container");
58058     /** false to disable window resize monitoring @type Boolean */
58059     this.monitorWindowResize = true;
58060     this.regions = {};
58061     this.addEvents({
58062         /**
58063          * @event layout
58064          * Fires when a layout is performed. 
58065          * @param {Roo.LayoutManager} this
58066          */
58067         "layout" : true,
58068         /**
58069          * @event regionresized
58070          * Fires when the user resizes a region. 
58071          * @param {Roo.LayoutRegion} region The resized region
58072          * @param {Number} newSize The new size (width for east/west, height for north/south)
58073          */
58074         "regionresized" : true,
58075         /**
58076          * @event regioncollapsed
58077          * Fires when a region is collapsed. 
58078          * @param {Roo.LayoutRegion} region The collapsed region
58079          */
58080         "regioncollapsed" : true,
58081         /**
58082          * @event regionexpanded
58083          * Fires when a region is expanded.  
58084          * @param {Roo.LayoutRegion} region The expanded region
58085          */
58086         "regionexpanded" : true
58087     });
58088     this.updating = false;
58089     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58090 };
58091
58092 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58093     /**
58094      * Returns true if this layout is currently being updated
58095      * @return {Boolean}
58096      */
58097     isUpdating : function(){
58098         return this.updating; 
58099     },
58100     
58101     /**
58102      * Suspend the LayoutManager from doing auto-layouts while
58103      * making multiple add or remove calls
58104      */
58105     beginUpdate : function(){
58106         this.updating = true;    
58107     },
58108     
58109     /**
58110      * Restore auto-layouts and optionally disable the manager from performing a layout
58111      * @param {Boolean} noLayout true to disable a layout update 
58112      */
58113     endUpdate : function(noLayout){
58114         this.updating = false;
58115         if(!noLayout){
58116             this.layout();
58117         }    
58118     },
58119     
58120     layout: function(){
58121         
58122     },
58123     
58124     onRegionResized : function(region, newSize){
58125         this.fireEvent("regionresized", region, newSize);
58126         this.layout();
58127     },
58128     
58129     onRegionCollapsed : function(region){
58130         this.fireEvent("regioncollapsed", region);
58131     },
58132     
58133     onRegionExpanded : function(region){
58134         this.fireEvent("regionexpanded", region);
58135     },
58136         
58137     /**
58138      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58139      * performs box-model adjustments.
58140      * @return {Object} The size as an object {width: (the width), height: (the height)}
58141      */
58142     getViewSize : function(){
58143         var size;
58144         if(this.el.dom != document.body){
58145             size = this.el.getSize();
58146         }else{
58147             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58148         }
58149         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58150         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58151         return size;
58152     },
58153     
58154     /**
58155      * Returns the Element this layout is bound to.
58156      * @return {Roo.Element}
58157      */
58158     getEl : function(){
58159         return this.el;
58160     },
58161     
58162     /**
58163      * Returns the specified region.
58164      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58165      * @return {Roo.LayoutRegion}
58166      */
58167     getRegion : function(target){
58168         return this.regions[target.toLowerCase()];
58169     },
58170     
58171     onWindowResize : function(){
58172         if(this.monitorWindowResize){
58173             this.layout();
58174         }
58175     }
58176 });/*
58177  * Based on:
58178  * Ext JS Library 1.1.1
58179  * Copyright(c) 2006-2007, Ext JS, LLC.
58180  *
58181  * Originally Released Under LGPL - original licence link has changed is not relivant.
58182  *
58183  * Fork - LGPL
58184  * <script type="text/javascript">
58185  */
58186 /**
58187  * @class Roo.BorderLayout
58188  * @extends Roo.LayoutManager
58189  * @children Roo.ContentPanel
58190  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58191  * please see: <br><br>
58192  * <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>
58193  * <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>
58194  * Example:
58195  <pre><code>
58196  var layout = new Roo.BorderLayout(document.body, {
58197     north: {
58198         initialSize: 25,
58199         titlebar: false
58200     },
58201     west: {
58202         split:true,
58203         initialSize: 200,
58204         minSize: 175,
58205         maxSize: 400,
58206         titlebar: true,
58207         collapsible: true
58208     },
58209     east: {
58210         split:true,
58211         initialSize: 202,
58212         minSize: 175,
58213         maxSize: 400,
58214         titlebar: true,
58215         collapsible: true
58216     },
58217     south: {
58218         split:true,
58219         initialSize: 100,
58220         minSize: 100,
58221         maxSize: 200,
58222         titlebar: true,
58223         collapsible: true
58224     },
58225     center: {
58226         titlebar: true,
58227         autoScroll:true,
58228         resizeTabs: true,
58229         minTabWidth: 50,
58230         preferredTabWidth: 150
58231     }
58232 });
58233
58234 // shorthand
58235 var CP = Roo.ContentPanel;
58236
58237 layout.beginUpdate();
58238 layout.add("north", new CP("north", "North"));
58239 layout.add("south", new CP("south", {title: "South", closable: true}));
58240 layout.add("west", new CP("west", {title: "West"}));
58241 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58242 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58243 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58244 layout.getRegion("center").showPanel("center1");
58245 layout.endUpdate();
58246 </code></pre>
58247
58248 <b>The container the layout is rendered into can be either the body element or any other element.
58249 If it is not the body element, the container needs to either be an absolute positioned element,
58250 or you will need to add "position:relative" to the css of the container.  You will also need to specify
58251 the container size if it is not the body element.</b>
58252
58253 * @constructor
58254 * Create a new BorderLayout
58255 * @param {String/HTMLElement/Element} container The container this layout is bound to
58256 * @param {Object} config Configuration options
58257  */
58258 Roo.BorderLayout = function(container, config){
58259     config = config || {};
58260     Roo.BorderLayout.superclass.constructor.call(this, container, config);
58261     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58262     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58263         var target = this.factory.validRegions[i];
58264         if(config[target]){
58265             this.addRegion(target, config[target]);
58266         }
58267     }
58268 };
58269
58270 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58271         
58272         /**
58273          * @cfg {Roo.LayoutRegion} east
58274          */
58275         /**
58276          * @cfg {Roo.LayoutRegion} west
58277          */
58278         /**
58279          * @cfg {Roo.LayoutRegion} north
58280          */
58281         /**
58282          * @cfg {Roo.LayoutRegion} south
58283          */
58284         /**
58285          * @cfg {Roo.LayoutRegion} center
58286          */
58287     /**
58288      * Creates and adds a new region if it doesn't already exist.
58289      * @param {String} target The target region key (north, south, east, west or center).
58290      * @param {Object} config The regions config object
58291      * @return {BorderLayoutRegion} The new region
58292      */
58293     addRegion : function(target, config){
58294         if(!this.regions[target]){
58295             var r = this.factory.create(target, this, config);
58296             this.bindRegion(target, r);
58297         }
58298         return this.regions[target];
58299     },
58300
58301     // private (kinda)
58302     bindRegion : function(name, r){
58303         this.regions[name] = r;
58304         r.on("visibilitychange", this.layout, this);
58305         r.on("paneladded", this.layout, this);
58306         r.on("panelremoved", this.layout, this);
58307         r.on("invalidated", this.layout, this);
58308         r.on("resized", this.onRegionResized, this);
58309         r.on("collapsed", this.onRegionCollapsed, this);
58310         r.on("expanded", this.onRegionExpanded, this);
58311     },
58312
58313     /**
58314      * Performs a layout update.
58315      */
58316     layout : function(){
58317         if(this.updating) {
58318             return;
58319         }
58320         var size = this.getViewSize();
58321         var w = size.width;
58322         var h = size.height;
58323         var centerW = w;
58324         var centerH = h;
58325         var centerY = 0;
58326         var centerX = 0;
58327         //var x = 0, y = 0;
58328
58329         var rs = this.regions;
58330         var north = rs["north"];
58331         var south = rs["south"]; 
58332         var west = rs["west"];
58333         var east = rs["east"];
58334         var center = rs["center"];
58335         //if(this.hideOnLayout){ // not supported anymore
58336             //c.el.setStyle("display", "none");
58337         //}
58338         if(north && north.isVisible()){
58339             var b = north.getBox();
58340             var m = north.getMargins();
58341             b.width = w - (m.left+m.right);
58342             b.x = m.left;
58343             b.y = m.top;
58344             centerY = b.height + b.y + m.bottom;
58345             centerH -= centerY;
58346             north.updateBox(this.safeBox(b));
58347         }
58348         if(south && south.isVisible()){
58349             var b = south.getBox();
58350             var m = south.getMargins();
58351             b.width = w - (m.left+m.right);
58352             b.x = m.left;
58353             var totalHeight = (b.height + m.top + m.bottom);
58354             b.y = h - totalHeight + m.top;
58355             centerH -= totalHeight;
58356             south.updateBox(this.safeBox(b));
58357         }
58358         if(west && west.isVisible()){
58359             var b = west.getBox();
58360             var m = west.getMargins();
58361             b.height = centerH - (m.top+m.bottom);
58362             b.x = m.left;
58363             b.y = centerY + m.top;
58364             var totalWidth = (b.width + m.left + m.right);
58365             centerX += totalWidth;
58366             centerW -= totalWidth;
58367             west.updateBox(this.safeBox(b));
58368         }
58369         if(east && east.isVisible()){
58370             var b = east.getBox();
58371             var m = east.getMargins();
58372             b.height = centerH - (m.top+m.bottom);
58373             var totalWidth = (b.width + m.left + m.right);
58374             b.x = w - totalWidth + m.left;
58375             b.y = centerY + m.top;
58376             centerW -= totalWidth;
58377             east.updateBox(this.safeBox(b));
58378         }
58379         if(center){
58380             var m = center.getMargins();
58381             var centerBox = {
58382                 x: centerX + m.left,
58383                 y: centerY + m.top,
58384                 width: centerW - (m.left+m.right),
58385                 height: centerH - (m.top+m.bottom)
58386             };
58387             //if(this.hideOnLayout){
58388                 //center.el.setStyle("display", "block");
58389             //}
58390             center.updateBox(this.safeBox(centerBox));
58391         }
58392         this.el.repaint();
58393         this.fireEvent("layout", this);
58394     },
58395
58396     // private
58397     safeBox : function(box){
58398         box.width = Math.max(0, box.width);
58399         box.height = Math.max(0, box.height);
58400         return box;
58401     },
58402
58403     /**
58404      * Adds a ContentPanel (or subclass) to this layout.
58405      * @param {String} target The target region key (north, south, east, west or center).
58406      * @param {Roo.ContentPanel} panel The panel to add
58407      * @return {Roo.ContentPanel} The added panel
58408      */
58409     add : function(target, panel){
58410          
58411         target = target.toLowerCase();
58412         return this.regions[target].add(panel);
58413     },
58414
58415     /**
58416      * Remove a ContentPanel (or subclass) to this layout.
58417      * @param {String} target The target region key (north, south, east, west or center).
58418      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58419      * @return {Roo.ContentPanel} The removed panel
58420      */
58421     remove : function(target, panel){
58422         target = target.toLowerCase();
58423         return this.regions[target].remove(panel);
58424     },
58425
58426     /**
58427      * Searches all regions for a panel with the specified id
58428      * @param {String} panelId
58429      * @return {Roo.ContentPanel} The panel or null if it wasn't found
58430      */
58431     findPanel : function(panelId){
58432         var rs = this.regions;
58433         for(var target in rs){
58434             if(typeof rs[target] != "function"){
58435                 var p = rs[target].getPanel(panelId);
58436                 if(p){
58437                     return p;
58438                 }
58439             }
58440         }
58441         return null;
58442     },
58443
58444     /**
58445      * Searches all regions for a panel with the specified id and activates (shows) it.
58446      * @param {String/ContentPanel} panelId The panels id or the panel itself
58447      * @return {Roo.ContentPanel} The shown panel or null
58448      */
58449     showPanel : function(panelId) {
58450       var rs = this.regions;
58451       for(var target in rs){
58452          var r = rs[target];
58453          if(typeof r != "function"){
58454             if(r.hasPanel(panelId)){
58455                return r.showPanel(panelId);
58456             }
58457          }
58458       }
58459       return null;
58460    },
58461
58462    /**
58463      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58464      * @param {Roo.state.Provider} provider (optional) An alternate state provider
58465      */
58466     restoreState : function(provider){
58467         if(!provider){
58468             provider = Roo.state.Manager;
58469         }
58470         var sm = new Roo.LayoutStateManager();
58471         sm.init(this, provider);
58472     },
58473
58474     /**
58475      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
58476      * object should contain properties for each region to add ContentPanels to, and each property's value should be
58477      * a valid ContentPanel config object.  Example:
58478      * <pre><code>
58479 // Create the main layout
58480 var layout = new Roo.BorderLayout('main-ct', {
58481     west: {
58482         split:true,
58483         minSize: 175,
58484         titlebar: true
58485     },
58486     center: {
58487         title:'Components'
58488     }
58489 }, 'main-ct');
58490
58491 // Create and add multiple ContentPanels at once via configs
58492 layout.batchAdd({
58493    west: {
58494        id: 'source-files',
58495        autoCreate:true,
58496        title:'Ext Source Files',
58497        autoScroll:true,
58498        fitToFrame:true
58499    },
58500    center : {
58501        el: cview,
58502        autoScroll:true,
58503        fitToFrame:true,
58504        toolbar: tb,
58505        resizeEl:'cbody'
58506    }
58507 });
58508 </code></pre>
58509      * @param {Object} regions An object containing ContentPanel configs by region name
58510      */
58511     batchAdd : function(regions){
58512         this.beginUpdate();
58513         for(var rname in regions){
58514             var lr = this.regions[rname];
58515             if(lr){
58516                 this.addTypedPanels(lr, regions[rname]);
58517             }
58518         }
58519         this.endUpdate();
58520     },
58521
58522     // private
58523     addTypedPanels : function(lr, ps){
58524         if(typeof ps == 'string'){
58525             lr.add(new Roo.ContentPanel(ps));
58526         }
58527         else if(ps instanceof Array){
58528             for(var i =0, len = ps.length; i < len; i++){
58529                 this.addTypedPanels(lr, ps[i]);
58530             }
58531         }
58532         else if(!ps.events){ // raw config?
58533             var el = ps.el;
58534             delete ps.el; // prevent conflict
58535             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58536         }
58537         else {  // panel object assumed!
58538             lr.add(ps);
58539         }
58540     },
58541     /**
58542      * Adds a xtype elements to the layout.
58543      * <pre><code>
58544
58545 layout.addxtype({
58546        xtype : 'ContentPanel',
58547        region: 'west',
58548        items: [ .... ]
58549    }
58550 );
58551
58552 layout.addxtype({
58553         xtype : 'NestedLayoutPanel',
58554         region: 'west',
58555         layout: {
58556            center: { },
58557            west: { }   
58558         },
58559         items : [ ... list of content panels or nested layout panels.. ]
58560    }
58561 );
58562 </code></pre>
58563      * @param {Object} cfg Xtype definition of item to add.
58564      */
58565     addxtype : function(cfg)
58566     {
58567         // basically accepts a pannel...
58568         // can accept a layout region..!?!?
58569         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58570         
58571         if (!cfg.xtype.match(/Panel$/)) {
58572             return false;
58573         }
58574         var ret = false;
58575         
58576         if (typeof(cfg.region) == 'undefined') {
58577             Roo.log("Failed to add Panel, region was not set");
58578             Roo.log(cfg);
58579             return false;
58580         }
58581         var region = cfg.region;
58582         delete cfg.region;
58583         
58584           
58585         var xitems = [];
58586         if (cfg.items) {
58587             xitems = cfg.items;
58588             delete cfg.items;
58589         }
58590         var nb = false;
58591         
58592         switch(cfg.xtype) 
58593         {
58594             case 'ContentPanel':  // ContentPanel (el, cfg)
58595             case 'ScrollPanel':  // ContentPanel (el, cfg)
58596             case 'ViewPanel': 
58597                 if(cfg.autoCreate) {
58598                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58599                 } else {
58600                     var el = this.el.createChild();
58601                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58602                 }
58603                 
58604                 this.add(region, ret);
58605                 break;
58606             
58607             
58608             case 'TreePanel': // our new panel!
58609                 cfg.el = this.el.createChild();
58610                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58611                 this.add(region, ret);
58612                 break;
58613             
58614             case 'NestedLayoutPanel': 
58615                 // create a new Layout (which is  a Border Layout...
58616                 var el = this.el.createChild();
58617                 var clayout = cfg.layout;
58618                 delete cfg.layout;
58619                 clayout.items   = clayout.items  || [];
58620                 // replace this exitems with the clayout ones..
58621                 xitems = clayout.items;
58622                  
58623                 
58624                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58625                     cfg.background = false;
58626                 }
58627                 var layout = new Roo.BorderLayout(el, clayout);
58628                 
58629                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58630                 //console.log('adding nested layout panel '  + cfg.toSource());
58631                 this.add(region, ret);
58632                 nb = {}; /// find first...
58633                 break;
58634                 
58635             case 'GridPanel': 
58636             
58637                 // needs grid and region
58638                 
58639                 //var el = this.getRegion(region).el.createChild();
58640                 var el = this.el.createChild();
58641                 // create the grid first...
58642                 
58643                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58644                 delete cfg.grid;
58645                 if (region == 'center' && this.active ) {
58646                     cfg.background = false;
58647                 }
58648                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58649                 
58650                 this.add(region, ret);
58651                 if (cfg.background) {
58652                     ret.on('activate', function(gp) {
58653                         if (!gp.grid.rendered) {
58654                             gp.grid.render();
58655                         }
58656                     });
58657                 } else {
58658                     grid.render();
58659                 }
58660                 break;
58661            
58662            
58663            
58664                 
58665                 
58666                 
58667             default:
58668                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58669                     
58670                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58671                     this.add(region, ret);
58672                 } else {
58673                 
58674                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58675                     return null;
58676                 }
58677                 
58678              // GridPanel (grid, cfg)
58679             
58680         }
58681         this.beginUpdate();
58682         // add children..
58683         var region = '';
58684         var abn = {};
58685         Roo.each(xitems, function(i)  {
58686             region = nb && i.region ? i.region : false;
58687             
58688             var add = ret.addxtype(i);
58689            
58690             if (region) {
58691                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58692                 if (!i.background) {
58693                     abn[region] = nb[region] ;
58694                 }
58695             }
58696             
58697         });
58698         this.endUpdate();
58699
58700         // make the last non-background panel active..
58701         //if (nb) { Roo.log(abn); }
58702         if (nb) {
58703             
58704             for(var r in abn) {
58705                 region = this.getRegion(r);
58706                 if (region) {
58707                     // tried using nb[r], but it does not work..
58708                      
58709                     region.showPanel(abn[r]);
58710                    
58711                 }
58712             }
58713         }
58714         return ret;
58715         
58716     }
58717 });
58718
58719 /**
58720  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58721  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
58722  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58723  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
58724  * <pre><code>
58725 // shorthand
58726 var CP = Roo.ContentPanel;
58727
58728 var layout = Roo.BorderLayout.create({
58729     north: {
58730         initialSize: 25,
58731         titlebar: false,
58732         panels: [new CP("north", "North")]
58733     },
58734     west: {
58735         split:true,
58736         initialSize: 200,
58737         minSize: 175,
58738         maxSize: 400,
58739         titlebar: true,
58740         collapsible: true,
58741         panels: [new CP("west", {title: "West"})]
58742     },
58743     east: {
58744         split:true,
58745         initialSize: 202,
58746         minSize: 175,
58747         maxSize: 400,
58748         titlebar: true,
58749         collapsible: true,
58750         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
58751     },
58752     south: {
58753         split:true,
58754         initialSize: 100,
58755         minSize: 100,
58756         maxSize: 200,
58757         titlebar: true,
58758         collapsible: true,
58759         panels: [new CP("south", {title: "South", closable: true})]
58760     },
58761     center: {
58762         titlebar: true,
58763         autoScroll:true,
58764         resizeTabs: true,
58765         minTabWidth: 50,
58766         preferredTabWidth: 150,
58767         panels: [
58768             new CP("center1", {title: "Close Me", closable: true}),
58769             new CP("center2", {title: "Center Panel", closable: false})
58770         ]
58771     }
58772 }, document.body);
58773
58774 layout.getRegion("center").showPanel("center1");
58775 </code></pre>
58776  * @param config
58777  * @param targetEl
58778  */
58779 Roo.BorderLayout.create = function(config, targetEl){
58780     var layout = new Roo.BorderLayout(targetEl || document.body, config);
58781     layout.beginUpdate();
58782     var regions = Roo.BorderLayout.RegionFactory.validRegions;
58783     for(var j = 0, jlen = regions.length; j < jlen; j++){
58784         var lr = regions[j];
58785         if(layout.regions[lr] && config[lr].panels){
58786             var r = layout.regions[lr];
58787             var ps = config[lr].panels;
58788             layout.addTypedPanels(r, ps);
58789         }
58790     }
58791     layout.endUpdate();
58792     return layout;
58793 };
58794
58795 // private
58796 Roo.BorderLayout.RegionFactory = {
58797     // private
58798     validRegions : ["north","south","east","west","center"],
58799
58800     // private
58801     create : function(target, mgr, config){
58802         target = target.toLowerCase();
58803         if(config.lightweight || config.basic){
58804             return new Roo.BasicLayoutRegion(mgr, config, target);
58805         }
58806         switch(target){
58807             case "north":
58808                 return new Roo.NorthLayoutRegion(mgr, config);
58809             case "south":
58810                 return new Roo.SouthLayoutRegion(mgr, config);
58811             case "east":
58812                 return new Roo.EastLayoutRegion(mgr, config);
58813             case "west":
58814                 return new Roo.WestLayoutRegion(mgr, config);
58815             case "center":
58816                 return new Roo.CenterLayoutRegion(mgr, config);
58817         }
58818         throw 'Layout region "'+target+'" not supported.';
58819     }
58820 };/*
58821  * Based on:
58822  * Ext JS Library 1.1.1
58823  * Copyright(c) 2006-2007, Ext JS, LLC.
58824  *
58825  * Originally Released Under LGPL - original licence link has changed is not relivant.
58826  *
58827  * Fork - LGPL
58828  * <script type="text/javascript">
58829  */
58830  
58831 /**
58832  * @class Roo.BasicLayoutRegion
58833  * @extends Roo.util.Observable
58834  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
58835  * and does not have a titlebar, tabs or any other features. All it does is size and position 
58836  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
58837  */
58838 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
58839     this.mgr = mgr;
58840     this.position  = pos;
58841     this.events = {
58842         /**
58843          * @scope Roo.BasicLayoutRegion
58844          */
58845         
58846         /**
58847          * @event beforeremove
58848          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
58849          * @param {Roo.LayoutRegion} this
58850          * @param {Roo.ContentPanel} panel The panel
58851          * @param {Object} e The cancel event object
58852          */
58853         "beforeremove" : true,
58854         /**
58855          * @event invalidated
58856          * Fires when the layout for this region is changed.
58857          * @param {Roo.LayoutRegion} this
58858          */
58859         "invalidated" : true,
58860         /**
58861          * @event visibilitychange
58862          * Fires when this region is shown or hidden 
58863          * @param {Roo.LayoutRegion} this
58864          * @param {Boolean} visibility true or false
58865          */
58866         "visibilitychange" : true,
58867         /**
58868          * @event paneladded
58869          * Fires when a panel is added. 
58870          * @param {Roo.LayoutRegion} this
58871          * @param {Roo.ContentPanel} panel The panel
58872          */
58873         "paneladded" : true,
58874         /**
58875          * @event panelremoved
58876          * Fires when a panel is removed. 
58877          * @param {Roo.LayoutRegion} this
58878          * @param {Roo.ContentPanel} panel The panel
58879          */
58880         "panelremoved" : true,
58881         /**
58882          * @event beforecollapse
58883          * Fires when this region before collapse.
58884          * @param {Roo.LayoutRegion} this
58885          */
58886         "beforecollapse" : true,
58887         /**
58888          * @event collapsed
58889          * Fires when this region is collapsed.
58890          * @param {Roo.LayoutRegion} this
58891          */
58892         "collapsed" : true,
58893         /**
58894          * @event expanded
58895          * Fires when this region is expanded.
58896          * @param {Roo.LayoutRegion} this
58897          */
58898         "expanded" : true,
58899         /**
58900          * @event slideshow
58901          * Fires when this region is slid into view.
58902          * @param {Roo.LayoutRegion} this
58903          */
58904         "slideshow" : true,
58905         /**
58906          * @event slidehide
58907          * Fires when this region slides out of view. 
58908          * @param {Roo.LayoutRegion} this
58909          */
58910         "slidehide" : true,
58911         /**
58912          * @event panelactivated
58913          * Fires when a panel is activated. 
58914          * @param {Roo.LayoutRegion} this
58915          * @param {Roo.ContentPanel} panel The activated panel
58916          */
58917         "panelactivated" : true,
58918         /**
58919          * @event resized
58920          * Fires when the user resizes this region. 
58921          * @param {Roo.LayoutRegion} this
58922          * @param {Number} newSize The new size (width for east/west, height for north/south)
58923          */
58924         "resized" : true
58925     };
58926     /** A collection of panels in this region. @type Roo.util.MixedCollection */
58927     this.panels = new Roo.util.MixedCollection();
58928     this.panels.getKey = this.getPanelId.createDelegate(this);
58929     this.box = null;
58930     this.activePanel = null;
58931     // ensure listeners are added...
58932     
58933     if (config.listeners || config.events) {
58934         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
58935             listeners : config.listeners || {},
58936             events : config.events || {}
58937         });
58938     }
58939     
58940     if(skipConfig !== true){
58941         this.applyConfig(config);
58942     }
58943 };
58944
58945 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
58946     getPanelId : function(p){
58947         return p.getId();
58948     },
58949     
58950     applyConfig : function(config){
58951         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
58952         this.config = config;
58953         
58954     },
58955     
58956     /**
58957      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
58958      * the width, for horizontal (north, south) the height.
58959      * @param {Number} newSize The new width or height
58960      */
58961     resizeTo : function(newSize){
58962         var el = this.el ? this.el :
58963                  (this.activePanel ? this.activePanel.getEl() : null);
58964         if(el){
58965             switch(this.position){
58966                 case "east":
58967                 case "west":
58968                     el.setWidth(newSize);
58969                     this.fireEvent("resized", this, newSize);
58970                 break;
58971                 case "north":
58972                 case "south":
58973                     el.setHeight(newSize);
58974                     this.fireEvent("resized", this, newSize);
58975                 break;                
58976             }
58977         }
58978     },
58979     
58980     getBox : function(){
58981         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
58982     },
58983     
58984     getMargins : function(){
58985         return this.margins;
58986     },
58987     
58988     updateBox : function(box){
58989         this.box = box;
58990         var el = this.activePanel.getEl();
58991         el.dom.style.left = box.x + "px";
58992         el.dom.style.top = box.y + "px";
58993         this.activePanel.setSize(box.width, box.height);
58994     },
58995     
58996     /**
58997      * Returns the container element for this region.
58998      * @return {Roo.Element}
58999      */
59000     getEl : function(){
59001         return this.activePanel;
59002     },
59003     
59004     /**
59005      * Returns true if this region is currently visible.
59006      * @return {Boolean}
59007      */
59008     isVisible : function(){
59009         return this.activePanel ? true : false;
59010     },
59011     
59012     setActivePanel : function(panel){
59013         panel = this.getPanel(panel);
59014         if(this.activePanel && this.activePanel != panel){
59015             this.activePanel.setActiveState(false);
59016             this.activePanel.getEl().setLeftTop(-10000,-10000);
59017         }
59018         this.activePanel = panel;
59019         panel.setActiveState(true);
59020         if(this.box){
59021             panel.setSize(this.box.width, this.box.height);
59022         }
59023         this.fireEvent("panelactivated", this, panel);
59024         this.fireEvent("invalidated");
59025     },
59026     
59027     /**
59028      * Show the specified panel.
59029      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59030      * @return {Roo.ContentPanel} The shown panel or null
59031      */
59032     showPanel : function(panel){
59033         if(panel = this.getPanel(panel)){
59034             this.setActivePanel(panel);
59035         }
59036         return panel;
59037     },
59038     
59039     /**
59040      * Get the active panel for this region.
59041      * @return {Roo.ContentPanel} The active panel or null
59042      */
59043     getActivePanel : function(){
59044         return this.activePanel;
59045     },
59046     
59047     /**
59048      * Add the passed ContentPanel(s)
59049      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59050      * @return {Roo.ContentPanel} The panel added (if only one was added)
59051      */
59052     add : function(panel){
59053         if(arguments.length > 1){
59054             for(var i = 0, len = arguments.length; i < len; i++) {
59055                 this.add(arguments[i]);
59056             }
59057             return null;
59058         }
59059         if(this.hasPanel(panel)){
59060             this.showPanel(panel);
59061             return panel;
59062         }
59063         var el = panel.getEl();
59064         if(el.dom.parentNode != this.mgr.el.dom){
59065             this.mgr.el.dom.appendChild(el.dom);
59066         }
59067         if(panel.setRegion){
59068             panel.setRegion(this);
59069         }
59070         this.panels.add(panel);
59071         el.setStyle("position", "absolute");
59072         if(!panel.background){
59073             this.setActivePanel(panel);
59074             if(this.config.initialSize && this.panels.getCount()==1){
59075                 this.resizeTo(this.config.initialSize);
59076             }
59077         }
59078         this.fireEvent("paneladded", this, panel);
59079         return panel;
59080     },
59081     
59082     /**
59083      * Returns true if the panel is in this region.
59084      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59085      * @return {Boolean}
59086      */
59087     hasPanel : function(panel){
59088         if(typeof panel == "object"){ // must be panel obj
59089             panel = panel.getId();
59090         }
59091         return this.getPanel(panel) ? true : false;
59092     },
59093     
59094     /**
59095      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59096      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59097      * @param {Boolean} preservePanel Overrides the config preservePanel option
59098      * @return {Roo.ContentPanel} The panel that was removed
59099      */
59100     remove : function(panel, preservePanel){
59101         panel = this.getPanel(panel);
59102         if(!panel){
59103             return null;
59104         }
59105         var e = {};
59106         this.fireEvent("beforeremove", this, panel, e);
59107         if(e.cancel === true){
59108             return null;
59109         }
59110         var panelId = panel.getId();
59111         this.panels.removeKey(panelId);
59112         return panel;
59113     },
59114     
59115     /**
59116      * Returns the panel specified or null if it's not in this region.
59117      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59118      * @return {Roo.ContentPanel}
59119      */
59120     getPanel : function(id){
59121         if(typeof id == "object"){ // must be panel obj
59122             return id;
59123         }
59124         return this.panels.get(id);
59125     },
59126     
59127     /**
59128      * Returns this regions position (north/south/east/west/center).
59129      * @return {String} 
59130      */
59131     getPosition: function(){
59132         return this.position;    
59133     }
59134 });/*
59135  * Based on:
59136  * Ext JS Library 1.1.1
59137  * Copyright(c) 2006-2007, Ext JS, LLC.
59138  *
59139  * Originally Released Under LGPL - original licence link has changed is not relivant.
59140  *
59141  * Fork - LGPL
59142  * <script type="text/javascript">
59143  */
59144  
59145 /**
59146  * @class Roo.LayoutRegion
59147  * @extends Roo.BasicLayoutRegion
59148  * This class represents a region in a layout manager.
59149  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
59150  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
59151  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
59152  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59153  * @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})
59154  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
59155  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
59156  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
59157  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
59158  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
59159  * @cfg {String}    title           The title for the region (overrides panel titles)
59160  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
59161  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59162  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
59163  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59164  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
59165  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59166  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
59167  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
59168  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
59169  * @cfg {Boolean}   showPin         True to show a pin button
59170  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
59171  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
59172  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
59173  * @cfg {Number}    width           For East/West panels
59174  * @cfg {Number}    height          For North/South panels
59175  * @cfg {Boolean}   split           To show the splitter
59176  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
59177  */
59178 Roo.LayoutRegion = function(mgr, config, pos){
59179     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59180     var dh = Roo.DomHelper;
59181     /** This region's container element 
59182     * @type Roo.Element */
59183     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59184     /** This region's title element 
59185     * @type Roo.Element */
59186
59187     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59188         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
59189         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59190     ]}, true);
59191     this.titleEl.enableDisplayMode();
59192     /** This region's title text element 
59193     * @type HTMLElement */
59194     this.titleTextEl = this.titleEl.dom.firstChild;
59195     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59196     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59197     this.closeBtn.enableDisplayMode();
59198     this.closeBtn.on("click", this.closeClicked, this);
59199     this.closeBtn.hide();
59200
59201     this.createBody(config);
59202     this.visible = true;
59203     this.collapsed = false;
59204
59205     if(config.hideWhenEmpty){
59206         this.hide();
59207         this.on("paneladded", this.validateVisibility, this);
59208         this.on("panelremoved", this.validateVisibility, this);
59209     }
59210     this.applyConfig(config);
59211 };
59212
59213 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59214
59215     createBody : function(){
59216         /** This region's body element 
59217         * @type Roo.Element */
59218         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59219     },
59220
59221     applyConfig : function(c){
59222         if(c.collapsible && this.position != "center" && !this.collapsedEl){
59223             var dh = Roo.DomHelper;
59224             if(c.titlebar !== false){
59225                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59226                 this.collapseBtn.on("click", this.collapse, this);
59227                 this.collapseBtn.enableDisplayMode();
59228
59229                 if(c.showPin === true || this.showPin){
59230                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59231                     this.stickBtn.enableDisplayMode();
59232                     this.stickBtn.on("click", this.expand, this);
59233                     this.stickBtn.hide();
59234                 }
59235             }
59236             /** This region's collapsed element
59237             * @type Roo.Element */
59238             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59239                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59240             ]}, true);
59241             if(c.floatable !== false){
59242                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59243                this.collapsedEl.on("click", this.collapseClick, this);
59244             }
59245
59246             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59247                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59248                    id: "message", unselectable: "on", style:{"float":"left"}});
59249                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59250              }
59251             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59252             this.expandBtn.on("click", this.expand, this);
59253         }
59254         if(this.collapseBtn){
59255             this.collapseBtn.setVisible(c.collapsible == true);
59256         }
59257         this.cmargins = c.cmargins || this.cmargins ||
59258                          (this.position == "west" || this.position == "east" ?
59259                              {top: 0, left: 2, right:2, bottom: 0} :
59260                              {top: 2, left: 0, right:0, bottom: 2});
59261         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59262         this.bottomTabs = c.tabPosition != "top";
59263         this.autoScroll = c.autoScroll || false;
59264         if(this.autoScroll){
59265             this.bodyEl.setStyle("overflow", "auto");
59266         }else{
59267             this.bodyEl.setStyle("overflow", "hidden");
59268         }
59269         //if(c.titlebar !== false){
59270             if((!c.titlebar && !c.title) || c.titlebar === false){
59271                 this.titleEl.hide();
59272             }else{
59273                 this.titleEl.show();
59274                 if(c.title){
59275                     this.titleTextEl.innerHTML = c.title;
59276                 }
59277             }
59278         //}
59279         this.duration = c.duration || .30;
59280         this.slideDuration = c.slideDuration || .45;
59281         this.config = c;
59282         if(c.collapsed){
59283             this.collapse(true);
59284         }
59285         if(c.hidden){
59286             this.hide();
59287         }
59288     },
59289     /**
59290      * Returns true if this region is currently visible.
59291      * @return {Boolean}
59292      */
59293     isVisible : function(){
59294         return this.visible;
59295     },
59296
59297     /**
59298      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59299      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
59300      */
59301     setCollapsedTitle : function(title){
59302         title = title || "&#160;";
59303         if(this.collapsedTitleTextEl){
59304             this.collapsedTitleTextEl.innerHTML = title;
59305         }
59306     },
59307
59308     getBox : function(){
59309         var b;
59310         if(!this.collapsed){
59311             b = this.el.getBox(false, true);
59312         }else{
59313             b = this.collapsedEl.getBox(false, true);
59314         }
59315         return b;
59316     },
59317
59318     getMargins : function(){
59319         return this.collapsed ? this.cmargins : this.margins;
59320     },
59321
59322     highlight : function(){
59323         this.el.addClass("x-layout-panel-dragover");
59324     },
59325
59326     unhighlight : function(){
59327         this.el.removeClass("x-layout-panel-dragover");
59328     },
59329
59330     updateBox : function(box){
59331         this.box = box;
59332         if(!this.collapsed){
59333             this.el.dom.style.left = box.x + "px";
59334             this.el.dom.style.top = box.y + "px";
59335             this.updateBody(box.width, box.height);
59336         }else{
59337             this.collapsedEl.dom.style.left = box.x + "px";
59338             this.collapsedEl.dom.style.top = box.y + "px";
59339             this.collapsedEl.setSize(box.width, box.height);
59340         }
59341         if(this.tabs){
59342             this.tabs.autoSizeTabs();
59343         }
59344     },
59345
59346     updateBody : function(w, h){
59347         if(w !== null){
59348             this.el.setWidth(w);
59349             w -= this.el.getBorderWidth("rl");
59350             if(this.config.adjustments){
59351                 w += this.config.adjustments[0];
59352             }
59353         }
59354         if(h !== null){
59355             this.el.setHeight(h);
59356             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59357             h -= this.el.getBorderWidth("tb");
59358             if(this.config.adjustments){
59359                 h += this.config.adjustments[1];
59360             }
59361             this.bodyEl.setHeight(h);
59362             if(this.tabs){
59363                 h = this.tabs.syncHeight(h);
59364             }
59365         }
59366         if(this.panelSize){
59367             w = w !== null ? w : this.panelSize.width;
59368             h = h !== null ? h : this.panelSize.height;
59369         }
59370         if(this.activePanel){
59371             var el = this.activePanel.getEl();
59372             w = w !== null ? w : el.getWidth();
59373             h = h !== null ? h : el.getHeight();
59374             this.panelSize = {width: w, height: h};
59375             this.activePanel.setSize(w, h);
59376         }
59377         if(Roo.isIE && this.tabs){
59378             this.tabs.el.repaint();
59379         }
59380     },
59381
59382     /**
59383      * Returns the container element for this region.
59384      * @return {Roo.Element}
59385      */
59386     getEl : function(){
59387         return this.el;
59388     },
59389
59390     /**
59391      * Hides this region.
59392      */
59393     hide : function(){
59394         if(!this.collapsed){
59395             this.el.dom.style.left = "-2000px";
59396             this.el.hide();
59397         }else{
59398             this.collapsedEl.dom.style.left = "-2000px";
59399             this.collapsedEl.hide();
59400         }
59401         this.visible = false;
59402         this.fireEvent("visibilitychange", this, false);
59403     },
59404
59405     /**
59406      * Shows this region if it was previously hidden.
59407      */
59408     show : function(){
59409         if(!this.collapsed){
59410             this.el.show();
59411         }else{
59412             this.collapsedEl.show();
59413         }
59414         this.visible = true;
59415         this.fireEvent("visibilitychange", this, true);
59416     },
59417
59418     closeClicked : function(){
59419         if(this.activePanel){
59420             this.remove(this.activePanel);
59421         }
59422     },
59423
59424     collapseClick : function(e){
59425         if(this.isSlid){
59426            e.stopPropagation();
59427            this.slideIn();
59428         }else{
59429            e.stopPropagation();
59430            this.slideOut();
59431         }
59432     },
59433
59434     /**
59435      * Collapses this region.
59436      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59437      */
59438     collapse : function(skipAnim, skipCheck){
59439         if(this.collapsed) {
59440             return;
59441         }
59442         
59443         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59444             
59445             this.collapsed = true;
59446             if(this.split){
59447                 this.split.el.hide();
59448             }
59449             if(this.config.animate && skipAnim !== true){
59450                 this.fireEvent("invalidated", this);
59451                 this.animateCollapse();
59452             }else{
59453                 this.el.setLocation(-20000,-20000);
59454                 this.el.hide();
59455                 this.collapsedEl.show();
59456                 this.fireEvent("collapsed", this);
59457                 this.fireEvent("invalidated", this);
59458             }
59459         }
59460         
59461     },
59462
59463     animateCollapse : function(){
59464         // overridden
59465     },
59466
59467     /**
59468      * Expands this region if it was previously collapsed.
59469      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59470      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59471      */
59472     expand : function(e, skipAnim){
59473         if(e) {
59474             e.stopPropagation();
59475         }
59476         if(!this.collapsed || this.el.hasActiveFx()) {
59477             return;
59478         }
59479         if(this.isSlid){
59480             this.afterSlideIn();
59481             skipAnim = true;
59482         }
59483         this.collapsed = false;
59484         if(this.config.animate && skipAnim !== true){
59485             this.animateExpand();
59486         }else{
59487             this.el.show();
59488             if(this.split){
59489                 this.split.el.show();
59490             }
59491             this.collapsedEl.setLocation(-2000,-2000);
59492             this.collapsedEl.hide();
59493             this.fireEvent("invalidated", this);
59494             this.fireEvent("expanded", this);
59495         }
59496     },
59497
59498     animateExpand : function(){
59499         // overridden
59500     },
59501
59502     initTabs : function()
59503     {
59504         this.bodyEl.setStyle("overflow", "hidden");
59505         var ts = new Roo.TabPanel(
59506                 this.bodyEl.dom,
59507                 {
59508                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
59509                     disableTooltips: this.config.disableTabTips,
59510                     toolbar : this.config.toolbar
59511                 }
59512         );
59513         if(this.config.hideTabs){
59514             ts.stripWrap.setDisplayed(false);
59515         }
59516         this.tabs = ts;
59517         ts.resizeTabs = this.config.resizeTabs === true;
59518         ts.minTabWidth = this.config.minTabWidth || 40;
59519         ts.maxTabWidth = this.config.maxTabWidth || 250;
59520         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59521         ts.monitorResize = false;
59522         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59523         ts.bodyEl.addClass('x-layout-tabs-body');
59524         this.panels.each(this.initPanelAsTab, this);
59525     },
59526
59527     initPanelAsTab : function(panel){
59528         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59529                     this.config.closeOnTab && panel.isClosable());
59530         if(panel.tabTip !== undefined){
59531             ti.setTooltip(panel.tabTip);
59532         }
59533         ti.on("activate", function(){
59534               this.setActivePanel(panel);
59535         }, this);
59536         if(this.config.closeOnTab){
59537             ti.on("beforeclose", function(t, e){
59538                 e.cancel = true;
59539                 this.remove(panel);
59540             }, this);
59541         }
59542         return ti;
59543     },
59544
59545     updatePanelTitle : function(panel, title){
59546         if(this.activePanel == panel){
59547             this.updateTitle(title);
59548         }
59549         if(this.tabs){
59550             var ti = this.tabs.getTab(panel.getEl().id);
59551             ti.setText(title);
59552             if(panel.tabTip !== undefined){
59553                 ti.setTooltip(panel.tabTip);
59554             }
59555         }
59556     },
59557
59558     updateTitle : function(title){
59559         if(this.titleTextEl && !this.config.title){
59560             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
59561         }
59562     },
59563
59564     setActivePanel : function(panel){
59565         panel = this.getPanel(panel);
59566         if(this.activePanel && this.activePanel != panel){
59567             this.activePanel.setActiveState(false);
59568         }
59569         this.activePanel = panel;
59570         panel.setActiveState(true);
59571         if(this.panelSize){
59572             panel.setSize(this.panelSize.width, this.panelSize.height);
59573         }
59574         if(this.closeBtn){
59575             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59576         }
59577         this.updateTitle(panel.getTitle());
59578         if(this.tabs){
59579             this.fireEvent("invalidated", this);
59580         }
59581         this.fireEvent("panelactivated", this, panel);
59582     },
59583
59584     /**
59585      * Shows the specified panel.
59586      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59587      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59588      */
59589     showPanel : function(panel)
59590     {
59591         panel = this.getPanel(panel);
59592         if(panel){
59593             if(this.tabs){
59594                 var tab = this.tabs.getTab(panel.getEl().id);
59595                 if(tab.isHidden()){
59596                     this.tabs.unhideTab(tab.id);
59597                 }
59598                 tab.activate();
59599             }else{
59600                 this.setActivePanel(panel);
59601             }
59602         }
59603         return panel;
59604     },
59605
59606     /**
59607      * Get the active panel for this region.
59608      * @return {Roo.ContentPanel} The active panel or null
59609      */
59610     getActivePanel : function(){
59611         return this.activePanel;
59612     },
59613
59614     validateVisibility : function(){
59615         if(this.panels.getCount() < 1){
59616             this.updateTitle("&#160;");
59617             this.closeBtn.hide();
59618             this.hide();
59619         }else{
59620             if(!this.isVisible()){
59621                 this.show();
59622             }
59623         }
59624     },
59625
59626     /**
59627      * Adds the passed ContentPanel(s) to this region.
59628      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59629      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59630      */
59631     add : function(panel){
59632         if(arguments.length > 1){
59633             for(var i = 0, len = arguments.length; i < len; i++) {
59634                 this.add(arguments[i]);
59635             }
59636             return null;
59637         }
59638         if(this.hasPanel(panel)){
59639             this.showPanel(panel);
59640             return panel;
59641         }
59642         panel.setRegion(this);
59643         this.panels.add(panel);
59644         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59645             this.bodyEl.dom.appendChild(panel.getEl().dom);
59646             if(panel.background !== true){
59647                 this.setActivePanel(panel);
59648             }
59649             this.fireEvent("paneladded", this, panel);
59650             return panel;
59651         }
59652         if(!this.tabs){
59653             this.initTabs();
59654         }else{
59655             this.initPanelAsTab(panel);
59656         }
59657         if(panel.background !== true){
59658             this.tabs.activate(panel.getEl().id);
59659         }
59660         this.fireEvent("paneladded", this, panel);
59661         return panel;
59662     },
59663
59664     /**
59665      * Hides the tab for the specified panel.
59666      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59667      */
59668     hidePanel : function(panel){
59669         if(this.tabs && (panel = this.getPanel(panel))){
59670             this.tabs.hideTab(panel.getEl().id);
59671         }
59672     },
59673
59674     /**
59675      * Unhides the tab for a previously hidden panel.
59676      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59677      */
59678     unhidePanel : function(panel){
59679         if(this.tabs && (panel = this.getPanel(panel))){
59680             this.tabs.unhideTab(panel.getEl().id);
59681         }
59682     },
59683
59684     clearPanels : function(){
59685         while(this.panels.getCount() > 0){
59686              this.remove(this.panels.first());
59687         }
59688     },
59689
59690     /**
59691      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59692      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59693      * @param {Boolean} preservePanel Overrides the config preservePanel option
59694      * @return {Roo.ContentPanel} The panel that was removed
59695      */
59696     remove : function(panel, preservePanel){
59697         panel = this.getPanel(panel);
59698         if(!panel){
59699             return null;
59700         }
59701         var e = {};
59702         this.fireEvent("beforeremove", this, panel, e);
59703         if(e.cancel === true){
59704             return null;
59705         }
59706         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59707         var panelId = panel.getId();
59708         this.panels.removeKey(panelId);
59709         if(preservePanel){
59710             document.body.appendChild(panel.getEl().dom);
59711         }
59712         if(this.tabs){
59713             this.tabs.removeTab(panel.getEl().id);
59714         }else if (!preservePanel){
59715             this.bodyEl.dom.removeChild(panel.getEl().dom);
59716         }
59717         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59718             var p = this.panels.first();
59719             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59720             tempEl.appendChild(p.getEl().dom);
59721             this.bodyEl.update("");
59722             this.bodyEl.dom.appendChild(p.getEl().dom);
59723             tempEl = null;
59724             this.updateTitle(p.getTitle());
59725             this.tabs = null;
59726             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59727             this.setActivePanel(p);
59728         }
59729         panel.setRegion(null);
59730         if(this.activePanel == panel){
59731             this.activePanel = null;
59732         }
59733         if(this.config.autoDestroy !== false && preservePanel !== true){
59734             try{panel.destroy();}catch(e){}
59735         }
59736         this.fireEvent("panelremoved", this, panel);
59737         return panel;
59738     },
59739
59740     /**
59741      * Returns the TabPanel component used by this region
59742      * @return {Roo.TabPanel}
59743      */
59744     getTabs : function(){
59745         return this.tabs;
59746     },
59747
59748     createTool : function(parentEl, className){
59749         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
59750             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
59751         btn.addClassOnOver("x-layout-tools-button-over");
59752         return btn;
59753     }
59754 });/*
59755  * Based on:
59756  * Ext JS Library 1.1.1
59757  * Copyright(c) 2006-2007, Ext JS, LLC.
59758  *
59759  * Originally Released Under LGPL - original licence link has changed is not relivant.
59760  *
59761  * Fork - LGPL
59762  * <script type="text/javascript">
59763  */
59764  
59765
59766
59767 /**
59768  * @class Roo.SplitLayoutRegion
59769  * @extends Roo.LayoutRegion
59770  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
59771  */
59772 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
59773     this.cursor = cursor;
59774     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
59775 };
59776
59777 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
59778     splitTip : "Drag to resize.",
59779     collapsibleSplitTip : "Drag to resize. Double click to hide.",
59780     useSplitTips : false,
59781
59782     applyConfig : function(config){
59783         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
59784         if(config.split){
59785             if(!this.split){
59786                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
59787                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
59788                 /** The SplitBar for this region 
59789                 * @type Roo.SplitBar */
59790                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
59791                 this.split.on("moved", this.onSplitMove, this);
59792                 this.split.useShim = config.useShim === true;
59793                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
59794                 if(this.useSplitTips){
59795                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
59796                 }
59797                 if(config.collapsible){
59798                     this.split.el.on("dblclick", this.collapse,  this);
59799                 }
59800             }
59801             if(typeof config.minSize != "undefined"){
59802                 this.split.minSize = config.minSize;
59803             }
59804             if(typeof config.maxSize != "undefined"){
59805                 this.split.maxSize = config.maxSize;
59806             }
59807             if(config.hideWhenEmpty || config.hidden || config.collapsed){
59808                 this.hideSplitter();
59809             }
59810         }
59811     },
59812
59813     getHMaxSize : function(){
59814          var cmax = this.config.maxSize || 10000;
59815          var center = this.mgr.getRegion("center");
59816          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
59817     },
59818
59819     getVMaxSize : function(){
59820          var cmax = this.config.maxSize || 10000;
59821          var center = this.mgr.getRegion("center");
59822          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
59823     },
59824
59825     onSplitMove : function(split, newSize){
59826         this.fireEvent("resized", this, newSize);
59827     },
59828     
59829     /** 
59830      * Returns the {@link Roo.SplitBar} for this region.
59831      * @return {Roo.SplitBar}
59832      */
59833     getSplitBar : function(){
59834         return this.split;
59835     },
59836     
59837     hide : function(){
59838         this.hideSplitter();
59839         Roo.SplitLayoutRegion.superclass.hide.call(this);
59840     },
59841
59842     hideSplitter : function(){
59843         if(this.split){
59844             this.split.el.setLocation(-2000,-2000);
59845             this.split.el.hide();
59846         }
59847     },
59848
59849     show : function(){
59850         if(this.split){
59851             this.split.el.show();
59852         }
59853         Roo.SplitLayoutRegion.superclass.show.call(this);
59854     },
59855     
59856     beforeSlide: function(){
59857         if(Roo.isGecko){// firefox overflow auto bug workaround
59858             this.bodyEl.clip();
59859             if(this.tabs) {
59860                 this.tabs.bodyEl.clip();
59861             }
59862             if(this.activePanel){
59863                 this.activePanel.getEl().clip();
59864                 
59865                 if(this.activePanel.beforeSlide){
59866                     this.activePanel.beforeSlide();
59867                 }
59868             }
59869         }
59870     },
59871     
59872     afterSlide : function(){
59873         if(Roo.isGecko){// firefox overflow auto bug workaround
59874             this.bodyEl.unclip();
59875             if(this.tabs) {
59876                 this.tabs.bodyEl.unclip();
59877             }
59878             if(this.activePanel){
59879                 this.activePanel.getEl().unclip();
59880                 if(this.activePanel.afterSlide){
59881                     this.activePanel.afterSlide();
59882                 }
59883             }
59884         }
59885     },
59886
59887     initAutoHide : function(){
59888         if(this.autoHide !== false){
59889             if(!this.autoHideHd){
59890                 var st = new Roo.util.DelayedTask(this.slideIn, this);
59891                 this.autoHideHd = {
59892                     "mouseout": function(e){
59893                         if(!e.within(this.el, true)){
59894                             st.delay(500);
59895                         }
59896                     },
59897                     "mouseover" : function(e){
59898                         st.cancel();
59899                     },
59900                     scope : this
59901                 };
59902             }
59903             this.el.on(this.autoHideHd);
59904         }
59905     },
59906
59907     clearAutoHide : function(){
59908         if(this.autoHide !== false){
59909             this.el.un("mouseout", this.autoHideHd.mouseout);
59910             this.el.un("mouseover", this.autoHideHd.mouseover);
59911         }
59912     },
59913
59914     clearMonitor : function(){
59915         Roo.get(document).un("click", this.slideInIf, this);
59916     },
59917
59918     // these names are backwards but not changed for compat
59919     slideOut : function(){
59920         if(this.isSlid || this.el.hasActiveFx()){
59921             return;
59922         }
59923         this.isSlid = true;
59924         if(this.collapseBtn){
59925             this.collapseBtn.hide();
59926         }
59927         this.closeBtnState = this.closeBtn.getStyle('display');
59928         this.closeBtn.hide();
59929         if(this.stickBtn){
59930             this.stickBtn.show();
59931         }
59932         this.el.show();
59933         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
59934         this.beforeSlide();
59935         this.el.setStyle("z-index", 10001);
59936         this.el.slideIn(this.getSlideAnchor(), {
59937             callback: function(){
59938                 this.afterSlide();
59939                 this.initAutoHide();
59940                 Roo.get(document).on("click", this.slideInIf, this);
59941                 this.fireEvent("slideshow", this);
59942             },
59943             scope: this,
59944             block: true
59945         });
59946     },
59947
59948     afterSlideIn : function(){
59949         this.clearAutoHide();
59950         this.isSlid = false;
59951         this.clearMonitor();
59952         this.el.setStyle("z-index", "");
59953         if(this.collapseBtn){
59954             this.collapseBtn.show();
59955         }
59956         this.closeBtn.setStyle('display', this.closeBtnState);
59957         if(this.stickBtn){
59958             this.stickBtn.hide();
59959         }
59960         this.fireEvent("slidehide", this);
59961     },
59962
59963     slideIn : function(cb){
59964         if(!this.isSlid || this.el.hasActiveFx()){
59965             Roo.callback(cb);
59966             return;
59967         }
59968         this.isSlid = false;
59969         this.beforeSlide();
59970         this.el.slideOut(this.getSlideAnchor(), {
59971             callback: function(){
59972                 this.el.setLeftTop(-10000, -10000);
59973                 this.afterSlide();
59974                 this.afterSlideIn();
59975                 Roo.callback(cb);
59976             },
59977             scope: this,
59978             block: true
59979         });
59980     },
59981     
59982     slideInIf : function(e){
59983         if(!e.within(this.el)){
59984             this.slideIn();
59985         }
59986     },
59987
59988     animateCollapse : function(){
59989         this.beforeSlide();
59990         this.el.setStyle("z-index", 20000);
59991         var anchor = this.getSlideAnchor();
59992         this.el.slideOut(anchor, {
59993             callback : function(){
59994                 this.el.setStyle("z-index", "");
59995                 this.collapsedEl.slideIn(anchor, {duration:.3});
59996                 this.afterSlide();
59997                 this.el.setLocation(-10000,-10000);
59998                 this.el.hide();
59999                 this.fireEvent("collapsed", this);
60000             },
60001             scope: this,
60002             block: true
60003         });
60004     },
60005
60006     animateExpand : function(){
60007         this.beforeSlide();
60008         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60009         this.el.setStyle("z-index", 20000);
60010         this.collapsedEl.hide({
60011             duration:.1
60012         });
60013         this.el.slideIn(this.getSlideAnchor(), {
60014             callback : function(){
60015                 this.el.setStyle("z-index", "");
60016                 this.afterSlide();
60017                 if(this.split){
60018                     this.split.el.show();
60019                 }
60020                 this.fireEvent("invalidated", this);
60021                 this.fireEvent("expanded", this);
60022             },
60023             scope: this,
60024             block: true
60025         });
60026     },
60027
60028     anchors : {
60029         "west" : "left",
60030         "east" : "right",
60031         "north" : "top",
60032         "south" : "bottom"
60033     },
60034
60035     sanchors : {
60036         "west" : "l",
60037         "east" : "r",
60038         "north" : "t",
60039         "south" : "b"
60040     },
60041
60042     canchors : {
60043         "west" : "tl-tr",
60044         "east" : "tr-tl",
60045         "north" : "tl-bl",
60046         "south" : "bl-tl"
60047     },
60048
60049     getAnchor : function(){
60050         return this.anchors[this.position];
60051     },
60052
60053     getCollapseAnchor : function(){
60054         return this.canchors[this.position];
60055     },
60056
60057     getSlideAnchor : function(){
60058         return this.sanchors[this.position];
60059     },
60060
60061     getAlignAdj : function(){
60062         var cm = this.cmargins;
60063         switch(this.position){
60064             case "west":
60065                 return [0, 0];
60066             break;
60067             case "east":
60068                 return [0, 0];
60069             break;
60070             case "north":
60071                 return [0, 0];
60072             break;
60073             case "south":
60074                 return [0, 0];
60075             break;
60076         }
60077     },
60078
60079     getExpandAdj : function(){
60080         var c = this.collapsedEl, cm = this.cmargins;
60081         switch(this.position){
60082             case "west":
60083                 return [-(cm.right+c.getWidth()+cm.left), 0];
60084             break;
60085             case "east":
60086                 return [cm.right+c.getWidth()+cm.left, 0];
60087             break;
60088             case "north":
60089                 return [0, -(cm.top+cm.bottom+c.getHeight())];
60090             break;
60091             case "south":
60092                 return [0, cm.top+cm.bottom+c.getHeight()];
60093             break;
60094         }
60095     }
60096 });/*
60097  * Based on:
60098  * Ext JS Library 1.1.1
60099  * Copyright(c) 2006-2007, Ext JS, LLC.
60100  *
60101  * Originally Released Under LGPL - original licence link has changed is not relivant.
60102  *
60103  * Fork - LGPL
60104  * <script type="text/javascript">
60105  */
60106 /*
60107  * These classes are private internal classes
60108  */
60109 Roo.CenterLayoutRegion = function(mgr, config){
60110     Roo.LayoutRegion.call(this, mgr, config, "center");
60111     this.visible = true;
60112     this.minWidth = config.minWidth || 20;
60113     this.minHeight = config.minHeight || 20;
60114 };
60115
60116 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60117     hide : function(){
60118         // center panel can't be hidden
60119     },
60120     
60121     show : function(){
60122         // center panel can't be hidden
60123     },
60124     
60125     getMinWidth: function(){
60126         return this.minWidth;
60127     },
60128     
60129     getMinHeight: function(){
60130         return this.minHeight;
60131     }
60132 });
60133
60134
60135 Roo.NorthLayoutRegion = function(mgr, config){
60136     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60137     if(this.split){
60138         this.split.placement = Roo.SplitBar.TOP;
60139         this.split.orientation = Roo.SplitBar.VERTICAL;
60140         this.split.el.addClass("x-layout-split-v");
60141     }
60142     var size = config.initialSize || config.height;
60143     if(typeof size != "undefined"){
60144         this.el.setHeight(size);
60145     }
60146 };
60147 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60148     orientation: Roo.SplitBar.VERTICAL,
60149     getBox : function(){
60150         if(this.collapsed){
60151             return this.collapsedEl.getBox();
60152         }
60153         var box = this.el.getBox();
60154         if(this.split){
60155             box.height += this.split.el.getHeight();
60156         }
60157         return box;
60158     },
60159     
60160     updateBox : function(box){
60161         if(this.split && !this.collapsed){
60162             box.height -= this.split.el.getHeight();
60163             this.split.el.setLeft(box.x);
60164             this.split.el.setTop(box.y+box.height);
60165             this.split.el.setWidth(box.width);
60166         }
60167         if(this.collapsed){
60168             this.updateBody(box.width, null);
60169         }
60170         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60171     }
60172 });
60173
60174 Roo.SouthLayoutRegion = function(mgr, config){
60175     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60176     if(this.split){
60177         this.split.placement = Roo.SplitBar.BOTTOM;
60178         this.split.orientation = Roo.SplitBar.VERTICAL;
60179         this.split.el.addClass("x-layout-split-v");
60180     }
60181     var size = config.initialSize || config.height;
60182     if(typeof size != "undefined"){
60183         this.el.setHeight(size);
60184     }
60185 };
60186 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60187     orientation: Roo.SplitBar.VERTICAL,
60188     getBox : function(){
60189         if(this.collapsed){
60190             return this.collapsedEl.getBox();
60191         }
60192         var box = this.el.getBox();
60193         if(this.split){
60194             var sh = this.split.el.getHeight();
60195             box.height += sh;
60196             box.y -= sh;
60197         }
60198         return box;
60199     },
60200     
60201     updateBox : function(box){
60202         if(this.split && !this.collapsed){
60203             var sh = this.split.el.getHeight();
60204             box.height -= sh;
60205             box.y += sh;
60206             this.split.el.setLeft(box.x);
60207             this.split.el.setTop(box.y-sh);
60208             this.split.el.setWidth(box.width);
60209         }
60210         if(this.collapsed){
60211             this.updateBody(box.width, null);
60212         }
60213         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60214     }
60215 });
60216
60217 Roo.EastLayoutRegion = function(mgr, config){
60218     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60219     if(this.split){
60220         this.split.placement = Roo.SplitBar.RIGHT;
60221         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60222         this.split.el.addClass("x-layout-split-h");
60223     }
60224     var size = config.initialSize || config.width;
60225     if(typeof size != "undefined"){
60226         this.el.setWidth(size);
60227     }
60228 };
60229 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60230     orientation: Roo.SplitBar.HORIZONTAL,
60231     getBox : function(){
60232         if(this.collapsed){
60233             return this.collapsedEl.getBox();
60234         }
60235         var box = this.el.getBox();
60236         if(this.split){
60237             var sw = this.split.el.getWidth();
60238             box.width += sw;
60239             box.x -= sw;
60240         }
60241         return box;
60242     },
60243
60244     updateBox : function(box){
60245         if(this.split && !this.collapsed){
60246             var sw = this.split.el.getWidth();
60247             box.width -= sw;
60248             this.split.el.setLeft(box.x);
60249             this.split.el.setTop(box.y);
60250             this.split.el.setHeight(box.height);
60251             box.x += sw;
60252         }
60253         if(this.collapsed){
60254             this.updateBody(null, box.height);
60255         }
60256         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60257     }
60258 });
60259
60260 Roo.WestLayoutRegion = function(mgr, config){
60261     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60262     if(this.split){
60263         this.split.placement = Roo.SplitBar.LEFT;
60264         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60265         this.split.el.addClass("x-layout-split-h");
60266     }
60267     var size = config.initialSize || config.width;
60268     if(typeof size != "undefined"){
60269         this.el.setWidth(size);
60270     }
60271 };
60272 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60273     orientation: Roo.SplitBar.HORIZONTAL,
60274     getBox : function(){
60275         if(this.collapsed){
60276             return this.collapsedEl.getBox();
60277         }
60278         var box = this.el.getBox();
60279         if(this.split){
60280             box.width += this.split.el.getWidth();
60281         }
60282         return box;
60283     },
60284     
60285     updateBox : function(box){
60286         if(this.split && !this.collapsed){
60287             var sw = this.split.el.getWidth();
60288             box.width -= sw;
60289             this.split.el.setLeft(box.x+box.width);
60290             this.split.el.setTop(box.y);
60291             this.split.el.setHeight(box.height);
60292         }
60293         if(this.collapsed){
60294             this.updateBody(null, box.height);
60295         }
60296         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60297     }
60298 });
60299 /*
60300  * Based on:
60301  * Ext JS Library 1.1.1
60302  * Copyright(c) 2006-2007, Ext JS, LLC.
60303  *
60304  * Originally Released Under LGPL - original licence link has changed is not relivant.
60305  *
60306  * Fork - LGPL
60307  * <script type="text/javascript">
60308  */
60309  
60310  
60311 /*
60312  * Private internal class for reading and applying state
60313  */
60314 Roo.LayoutStateManager = function(layout){
60315      // default empty state
60316      this.state = {
60317         north: {},
60318         south: {},
60319         east: {},
60320         west: {}       
60321     };
60322 };
60323
60324 Roo.LayoutStateManager.prototype = {
60325     init : function(layout, provider){
60326         this.provider = provider;
60327         var state = provider.get(layout.id+"-layout-state");
60328         if(state){
60329             var wasUpdating = layout.isUpdating();
60330             if(!wasUpdating){
60331                 layout.beginUpdate();
60332             }
60333             for(var key in state){
60334                 if(typeof state[key] != "function"){
60335                     var rstate = state[key];
60336                     var r = layout.getRegion(key);
60337                     if(r && rstate){
60338                         if(rstate.size){
60339                             r.resizeTo(rstate.size);
60340                         }
60341                         if(rstate.collapsed == true){
60342                             r.collapse(true);
60343                         }else{
60344                             r.expand(null, true);
60345                         }
60346                     }
60347                 }
60348             }
60349             if(!wasUpdating){
60350                 layout.endUpdate();
60351             }
60352             this.state = state; 
60353         }
60354         this.layout = layout;
60355         layout.on("regionresized", this.onRegionResized, this);
60356         layout.on("regioncollapsed", this.onRegionCollapsed, this);
60357         layout.on("regionexpanded", this.onRegionExpanded, this);
60358     },
60359     
60360     storeState : function(){
60361         this.provider.set(this.layout.id+"-layout-state", this.state);
60362     },
60363     
60364     onRegionResized : function(region, newSize){
60365         this.state[region.getPosition()].size = newSize;
60366         this.storeState();
60367     },
60368     
60369     onRegionCollapsed : function(region){
60370         this.state[region.getPosition()].collapsed = true;
60371         this.storeState();
60372     },
60373     
60374     onRegionExpanded : function(region){
60375         this.state[region.getPosition()].collapsed = false;
60376         this.storeState();
60377     }
60378 };/*
60379  * Based on:
60380  * Ext JS Library 1.1.1
60381  * Copyright(c) 2006-2007, Ext JS, LLC.
60382  *
60383  * Originally Released Under LGPL - original licence link has changed is not relivant.
60384  *
60385  * Fork - LGPL
60386  * <script type="text/javascript">
60387  */
60388 /**
60389  * @class Roo.ContentPanel
60390  * @extends Roo.util.Observable
60391  * @children Roo.form.Form Roo.JsonView Roo.View
60392  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60393  * A basic ContentPanel element.
60394  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
60395  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
60396  * @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
60397  * @cfg {Boolean}   closable      True if the panel can be closed/removed
60398  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
60399  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60400  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
60401  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
60402  * @cfg {String} title          The title for this panel
60403  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60404  * @cfg {String} url            Calls {@link #setUrl} with this value
60405  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60406  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
60407  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
60408  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
60409  * @cfg {String}    style  Extra style to add to the content panel
60410  * @cfg {Roo.menu.Menu} menu  popup menu
60411
60412  * @constructor
60413  * Create a new ContentPanel.
60414  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60415  * @param {String/Object} config A string to set only the title or a config object
60416  * @param {String} content (optional) Set the HTML content for this panel
60417  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60418  */
60419 Roo.ContentPanel = function(el, config, content){
60420     
60421      
60422     /*
60423     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60424         config = el;
60425         el = Roo.id();
60426     }
60427     if (config && config.parentLayout) { 
60428         el = config.parentLayout.el.createChild(); 
60429     }
60430     */
60431     if(el.autoCreate){ // xtype is available if this is called from factory
60432         config = el;
60433         el = Roo.id();
60434     }
60435     this.el = Roo.get(el);
60436     if(!this.el && config && config.autoCreate){
60437         if(typeof config.autoCreate == "object"){
60438             if(!config.autoCreate.id){
60439                 config.autoCreate.id = config.id||el;
60440             }
60441             this.el = Roo.DomHelper.append(document.body,
60442                         config.autoCreate, true);
60443         }else{
60444             this.el = Roo.DomHelper.append(document.body,
60445                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60446         }
60447     }
60448     
60449     
60450     this.closable = false;
60451     this.loaded = false;
60452     this.active = false;
60453     if(typeof config == "string"){
60454         this.title = config;
60455     }else{
60456         Roo.apply(this, config);
60457     }
60458     
60459     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60460         this.wrapEl = this.el.wrap();
60461         this.toolbar.container = this.el.insertSibling(false, 'before');
60462         this.toolbar = new Roo.Toolbar(this.toolbar);
60463     }
60464     
60465     // xtype created footer. - not sure if will work as we normally have to render first..
60466     if (this.footer && !this.footer.el && this.footer.xtype) {
60467         if (!this.wrapEl) {
60468             this.wrapEl = this.el.wrap();
60469         }
60470     
60471         this.footer.container = this.wrapEl.createChild();
60472          
60473         this.footer = Roo.factory(this.footer, Roo);
60474         
60475     }
60476     
60477     if(this.resizeEl){
60478         this.resizeEl = Roo.get(this.resizeEl, true);
60479     }else{
60480         this.resizeEl = this.el;
60481     }
60482     // handle view.xtype
60483     
60484  
60485     
60486     
60487     this.addEvents({
60488         /**
60489          * @event activate
60490          * Fires when this panel is activated. 
60491          * @param {Roo.ContentPanel} this
60492          */
60493         "activate" : true,
60494         /**
60495          * @event deactivate
60496          * Fires when this panel is activated. 
60497          * @param {Roo.ContentPanel} this
60498          */
60499         "deactivate" : true,
60500
60501         /**
60502          * @event resize
60503          * Fires when this panel is resized if fitToFrame is true.
60504          * @param {Roo.ContentPanel} this
60505          * @param {Number} width The width after any component adjustments
60506          * @param {Number} height The height after any component adjustments
60507          */
60508         "resize" : true,
60509         
60510          /**
60511          * @event render
60512          * Fires when this tab is created
60513          * @param {Roo.ContentPanel} this
60514          */
60515         "render" : true
60516          
60517         
60518     });
60519     
60520
60521     
60522     
60523     if(this.autoScroll){
60524         this.resizeEl.setStyle("overflow", "auto");
60525     } else {
60526         // fix randome scrolling
60527         this.el.on('scroll', function() {
60528             Roo.log('fix random scolling');
60529             this.scrollTo('top',0); 
60530         });
60531     }
60532     content = content || this.content;
60533     if(content){
60534         this.setContent(content);
60535     }
60536     if(config && config.url){
60537         this.setUrl(this.url, this.params, this.loadOnce);
60538     }
60539     
60540     
60541     
60542     Roo.ContentPanel.superclass.constructor.call(this);
60543     
60544     if (this.view && typeof(this.view.xtype) != 'undefined') {
60545         this.view.el = this.el.appendChild(document.createElement("div"));
60546         this.view = Roo.factory(this.view); 
60547         this.view.render  &&  this.view.render(false, '');  
60548     }
60549     
60550     
60551     this.fireEvent('render', this);
60552 };
60553
60554 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60555     tabTip:'',
60556     setRegion : function(region){
60557         this.region = region;
60558         if(region){
60559            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60560         }else{
60561            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60562         } 
60563     },
60564     
60565     /**
60566      * Returns the toolbar for this Panel if one was configured. 
60567      * @return {Roo.Toolbar} 
60568      */
60569     getToolbar : function(){
60570         return this.toolbar;
60571     },
60572     
60573     setActiveState : function(active){
60574         this.active = active;
60575         if(!active){
60576             this.fireEvent("deactivate", this);
60577         }else{
60578             this.fireEvent("activate", this);
60579         }
60580     },
60581     /**
60582      * Updates this panel's element
60583      * @param {String} content The new content
60584      * @param {Boolean} loadScripts (optional) true to look for and process scripts
60585     */
60586     setContent : function(content, loadScripts){
60587         this.el.update(content, loadScripts);
60588     },
60589
60590     ignoreResize : function(w, h){
60591         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60592             return true;
60593         }else{
60594             this.lastSize = {width: w, height: h};
60595             return false;
60596         }
60597     },
60598     /**
60599      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60600      * @return {Roo.UpdateManager} The UpdateManager
60601      */
60602     getUpdateManager : function(){
60603         return this.el.getUpdateManager();
60604     },
60605      /**
60606      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60607      * @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:
60608 <pre><code>
60609 panel.load({
60610     url: "your-url.php",
60611     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60612     callback: yourFunction,
60613     scope: yourObject, //(optional scope)
60614     discardUrl: false,
60615     nocache: false,
60616     text: "Loading...",
60617     timeout: 30,
60618     scripts: false
60619 });
60620 </code></pre>
60621      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60622      * 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.
60623      * @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}
60624      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60625      * @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.
60626      * @return {Roo.ContentPanel} this
60627      */
60628     load : function(){
60629         var um = this.el.getUpdateManager();
60630         um.update.apply(um, arguments);
60631         return this;
60632     },
60633
60634
60635     /**
60636      * 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.
60637      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60638      * @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)
60639      * @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)
60640      * @return {Roo.UpdateManager} The UpdateManager
60641      */
60642     setUrl : function(url, params, loadOnce){
60643         if(this.refreshDelegate){
60644             this.removeListener("activate", this.refreshDelegate);
60645         }
60646         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60647         this.on("activate", this.refreshDelegate);
60648         return this.el.getUpdateManager();
60649     },
60650     
60651     _handleRefresh : function(url, params, loadOnce){
60652         if(!loadOnce || !this.loaded){
60653             var updater = this.el.getUpdateManager();
60654             updater.update(url, params, this._setLoaded.createDelegate(this));
60655         }
60656     },
60657     
60658     _setLoaded : function(){
60659         this.loaded = true;
60660     }, 
60661     
60662     /**
60663      * Returns this panel's id
60664      * @return {String} 
60665      */
60666     getId : function(){
60667         return this.el.id;
60668     },
60669     
60670     /** 
60671      * Returns this panel's element - used by regiosn to add.
60672      * @return {Roo.Element} 
60673      */
60674     getEl : function(){
60675         return this.wrapEl || this.el;
60676     },
60677     
60678     adjustForComponents : function(width, height)
60679     {
60680         //Roo.log('adjustForComponents ');
60681         if(this.resizeEl != this.el){
60682             width -= this.el.getFrameWidth('lr');
60683             height -= this.el.getFrameWidth('tb');
60684         }
60685         if(this.toolbar){
60686             var te = this.toolbar.getEl();
60687             height -= te.getHeight();
60688             te.setWidth(width);
60689         }
60690         if(this.footer){
60691             var te = this.footer.getEl();
60692             //Roo.log("footer:" + te.getHeight());
60693             
60694             height -= te.getHeight();
60695             te.setWidth(width);
60696         }
60697         
60698         
60699         if(this.adjustments){
60700             width += this.adjustments[0];
60701             height += this.adjustments[1];
60702         }
60703         return {"width": width, "height": height};
60704     },
60705     
60706     setSize : function(width, height){
60707         if(this.fitToFrame && !this.ignoreResize(width, height)){
60708             if(this.fitContainer && this.resizeEl != this.el){
60709                 this.el.setSize(width, height);
60710             }
60711             var size = this.adjustForComponents(width, height);
60712             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60713             this.fireEvent('resize', this, size.width, size.height);
60714         }
60715     },
60716     
60717     /**
60718      * Returns this panel's title
60719      * @return {String} 
60720      */
60721     getTitle : function(){
60722         return this.title;
60723     },
60724     
60725     /**
60726      * Set this panel's title
60727      * @param {String} title
60728      */
60729     setTitle : function(title){
60730         this.title = title;
60731         if(this.region){
60732             this.region.updatePanelTitle(this, title);
60733         }
60734     },
60735     
60736     /**
60737      * Returns true is this panel was configured to be closable
60738      * @return {Boolean} 
60739      */
60740     isClosable : function(){
60741         return this.closable;
60742     },
60743     
60744     beforeSlide : function(){
60745         this.el.clip();
60746         this.resizeEl.clip();
60747     },
60748     
60749     afterSlide : function(){
60750         this.el.unclip();
60751         this.resizeEl.unclip();
60752     },
60753     
60754     /**
60755      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
60756      *   Will fail silently if the {@link #setUrl} method has not been called.
60757      *   This does not activate the panel, just updates its content.
60758      */
60759     refresh : function(){
60760         if(this.refreshDelegate){
60761            this.loaded = false;
60762            this.refreshDelegate();
60763         }
60764     },
60765     
60766     /**
60767      * Destroys this panel
60768      */
60769     destroy : function(){
60770         this.el.removeAllListeners();
60771         var tempEl = document.createElement("span");
60772         tempEl.appendChild(this.el.dom);
60773         tempEl.innerHTML = "";
60774         this.el.remove();
60775         this.el = null;
60776     },
60777     
60778     /**
60779      * form - if the content panel contains a form - this is a reference to it.
60780      * @type {Roo.form.Form}
60781      */
60782     form : false,
60783     /**
60784      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
60785      *    This contains a reference to it.
60786      * @type {Roo.View}
60787      */
60788     view : false,
60789     
60790       /**
60791      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
60792      * <pre><code>
60793
60794 layout.addxtype({
60795        xtype : 'Form',
60796        items: [ .... ]
60797    }
60798 );
60799
60800 </code></pre>
60801      * @param {Object} cfg Xtype definition of item to add.
60802      */
60803     
60804     addxtype : function(cfg) {
60805         // add form..
60806         if (cfg.xtype.match(/^Form$/)) {
60807             
60808             var el;
60809             //if (this.footer) {
60810             //    el = this.footer.container.insertSibling(false, 'before');
60811             //} else {
60812                 el = this.el.createChild();
60813             //}
60814
60815             this.form = new  Roo.form.Form(cfg);
60816             
60817             
60818             if ( this.form.allItems.length) {
60819                 this.form.render(el.dom);
60820             }
60821             return this.form;
60822         }
60823         // should only have one of theses..
60824         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
60825             // views.. should not be just added - used named prop 'view''
60826             
60827             cfg.el = this.el.appendChild(document.createElement("div"));
60828             // factory?
60829             
60830             var ret = new Roo.factory(cfg);
60831              
60832              ret.render && ret.render(false, ''); // render blank..
60833             this.view = ret;
60834             return ret;
60835         }
60836         return false;
60837     }
60838 });
60839
60840
60841
60842
60843
60844
60845
60846
60847
60848
60849
60850
60851 /**
60852  * @class Roo.GridPanel
60853  * @extends Roo.ContentPanel
60854  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60855  * @constructor
60856  * Create a new GridPanel.
60857  * @cfg {Roo.grid.Grid} grid The grid for this panel
60858  */
60859 Roo.GridPanel = function(grid, config){
60860     
60861     // universal ctor...
60862     if (typeof(grid.grid) != 'undefined') {
60863         config = grid;
60864         grid = config.grid;
60865     }
60866     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
60867         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
60868         
60869     this.wrapper.dom.appendChild(grid.getGridEl().dom);
60870     
60871     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
60872     
60873     if(this.toolbar){
60874         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
60875     }
60876     // xtype created footer. - not sure if will work as we normally have to render first..
60877     if (this.footer && !this.footer.el && this.footer.xtype) {
60878         
60879         this.footer.container = this.grid.getView().getFooterPanel(true);
60880         this.footer.dataSource = this.grid.dataSource;
60881         this.footer = Roo.factory(this.footer, Roo);
60882         
60883     }
60884     
60885     grid.monitorWindowResize = false; // turn off autosizing
60886     grid.autoHeight = false;
60887     grid.autoWidth = false;
60888     this.grid = grid;
60889     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
60890 };
60891
60892 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
60893     getId : function(){
60894         return this.grid.id;
60895     },
60896     
60897     /**
60898      * Returns the grid for this panel
60899      * @return {Roo.grid.Grid} 
60900      */
60901     getGrid : function(){
60902         return this.grid;    
60903     },
60904     
60905     setSize : function(width, height){
60906         if(!this.ignoreResize(width, height)){
60907             var grid = this.grid;
60908             var size = this.adjustForComponents(width, height);
60909             grid.getGridEl().setSize(size.width, size.height);
60910             grid.autoSize();
60911         }
60912     },
60913     
60914     beforeSlide : function(){
60915         this.grid.getView().scroller.clip();
60916     },
60917     
60918     afterSlide : function(){
60919         this.grid.getView().scroller.unclip();
60920     },
60921     
60922     destroy : function(){
60923         this.grid.destroy();
60924         delete this.grid;
60925         Roo.GridPanel.superclass.destroy.call(this); 
60926     }
60927 });
60928
60929
60930 /**
60931  * @class Roo.NestedLayoutPanel
60932  * @extends Roo.ContentPanel
60933  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60934  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
60935  *
60936  * 
60937  * @constructor
60938  * Create a new NestedLayoutPanel.
60939  * 
60940  * 
60941  * @param {Roo.BorderLayout} layout [required] The layout for this panel
60942  * @param {String/Object} config A string to set only the title or a config object
60943  */
60944 Roo.NestedLayoutPanel = function(layout, config)
60945 {
60946     // construct with only one argument..
60947     /* FIXME - implement nicer consturctors
60948     if (layout.layout) {
60949         config = layout;
60950         layout = config.layout;
60951         delete config.layout;
60952     }
60953     if (layout.xtype && !layout.getEl) {
60954         // then layout needs constructing..
60955         layout = Roo.factory(layout, Roo);
60956     }
60957     */
60958     
60959     
60960     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
60961     
60962     layout.monitorWindowResize = false; // turn off autosizing
60963     this.layout = layout;
60964     this.layout.getEl().addClass("x-layout-nested-layout");
60965     
60966     
60967     
60968     
60969 };
60970
60971 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
60972
60973     layout : false,
60974
60975     setSize : function(width, height){
60976         if(!this.ignoreResize(width, height)){
60977             var size = this.adjustForComponents(width, height);
60978             var el = this.layout.getEl();
60979             el.setSize(size.width, size.height);
60980             var touch = el.dom.offsetWidth;
60981             this.layout.layout();
60982             // ie requires a double layout on the first pass
60983             if(Roo.isIE && !this.initialized){
60984                 this.initialized = true;
60985                 this.layout.layout();
60986             }
60987         }
60988     },
60989     
60990     // activate all subpanels if not currently active..
60991     
60992     setActiveState : function(active){
60993         this.active = active;
60994         if(!active){
60995             this.fireEvent("deactivate", this);
60996             return;
60997         }
60998         
60999         this.fireEvent("activate", this);
61000         // not sure if this should happen before or after..
61001         if (!this.layout) {
61002             return; // should not happen..
61003         }
61004         var reg = false;
61005         for (var r in this.layout.regions) {
61006             reg = this.layout.getRegion(r);
61007             if (reg.getActivePanel()) {
61008                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
61009                 reg.setActivePanel(reg.getActivePanel());
61010                 continue;
61011             }
61012             if (!reg.panels.length) {
61013                 continue;
61014             }
61015             reg.showPanel(reg.getPanel(0));
61016         }
61017         
61018         
61019         
61020         
61021     },
61022     
61023     /**
61024      * Returns the nested BorderLayout for this panel
61025      * @return {Roo.BorderLayout}
61026      */
61027     getLayout : function(){
61028         return this.layout;
61029     },
61030     
61031      /**
61032      * Adds a xtype elements to the layout of the nested panel
61033      * <pre><code>
61034
61035 panel.addxtype({
61036        xtype : 'ContentPanel',
61037        region: 'west',
61038        items: [ .... ]
61039    }
61040 );
61041
61042 panel.addxtype({
61043         xtype : 'NestedLayoutPanel',
61044         region: 'west',
61045         layout: {
61046            center: { },
61047            west: { }   
61048         },
61049         items : [ ... list of content panels or nested layout panels.. ]
61050    }
61051 );
61052 </code></pre>
61053      * @param {Object} cfg Xtype definition of item to add.
61054      */
61055     addxtype : function(cfg) {
61056         return this.layout.addxtype(cfg);
61057     
61058     }
61059 });
61060
61061 Roo.ScrollPanel = function(el, config, content){
61062     config = config || {};
61063     config.fitToFrame = true;
61064     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61065     
61066     this.el.dom.style.overflow = "hidden";
61067     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61068     this.el.removeClass("x-layout-inactive-content");
61069     this.el.on("mousewheel", this.onWheel, this);
61070
61071     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
61072     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
61073     up.unselectable(); down.unselectable();
61074     up.on("click", this.scrollUp, this);
61075     down.on("click", this.scrollDown, this);
61076     up.addClassOnOver("x-scroller-btn-over");
61077     down.addClassOnOver("x-scroller-btn-over");
61078     up.addClassOnClick("x-scroller-btn-click");
61079     down.addClassOnClick("x-scroller-btn-click");
61080     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61081
61082     this.resizeEl = this.el;
61083     this.el = wrap; this.up = up; this.down = down;
61084 };
61085
61086 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61087     increment : 100,
61088     wheelIncrement : 5,
61089     scrollUp : function(){
61090         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61091     },
61092
61093     scrollDown : function(){
61094         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61095     },
61096
61097     afterScroll : function(){
61098         var el = this.resizeEl;
61099         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61100         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61101         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61102     },
61103
61104     setSize : function(){
61105         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61106         this.afterScroll();
61107     },
61108
61109     onWheel : function(e){
61110         var d = e.getWheelDelta();
61111         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61112         this.afterScroll();
61113         e.stopEvent();
61114     },
61115
61116     setContent : function(content, loadScripts){
61117         this.resizeEl.update(content, loadScripts);
61118     }
61119
61120 });
61121
61122
61123
61124 /**
61125  * @class Roo.TreePanel
61126  * @extends Roo.ContentPanel
61127  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61128  * Treepanel component
61129  * 
61130  * @constructor
61131  * Create a new TreePanel. - defaults to fit/scoll contents.
61132  * @param {String/Object} config A string to set only the panel's title, or a config object
61133  */
61134 Roo.TreePanel = function(config){
61135     var el = config.el;
61136     var tree = config.tree;
61137     delete config.tree; 
61138     delete config.el; // hopefull!
61139     
61140     // wrapper for IE7 strict & safari scroll issue
61141     
61142     var treeEl = el.createChild();
61143     config.resizeEl = treeEl;
61144     
61145     
61146     
61147     Roo.TreePanel.superclass.constructor.call(this, el, config);
61148  
61149  
61150     this.tree = new Roo.tree.TreePanel(treeEl , tree);
61151     //console.log(tree);
61152     this.on('activate', function()
61153     {
61154         if (this.tree.rendered) {
61155             return;
61156         }
61157         //console.log('render tree');
61158         this.tree.render();
61159     });
61160     // this should not be needed.. - it's actually the 'el' that resizes?
61161     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61162     
61163     //this.on('resize',  function (cp, w, h) {
61164     //        this.tree.innerCt.setWidth(w);
61165     //        this.tree.innerCt.setHeight(h);
61166     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
61167     //});
61168
61169         
61170     
61171 };
61172
61173 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
61174     fitToFrame : true,
61175     autoScroll : true,
61176     /*
61177      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61178      */
61179     tree : false
61180
61181 });
61182 /*
61183  * Based on:
61184  * Ext JS Library 1.1.1
61185  * Copyright(c) 2006-2007, Ext JS, LLC.
61186  *
61187  * Originally Released Under LGPL - original licence link has changed is not relivant.
61188  *
61189  * Fork - LGPL
61190  * <script type="text/javascript">
61191  */
61192  
61193
61194 /**
61195  * @class Roo.ReaderLayout
61196  * @extends Roo.BorderLayout
61197  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
61198  * center region containing two nested regions (a top one for a list view and one for item preview below),
61199  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61200  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61201  * expedites the setup of the overall layout and regions for this common application style.
61202  * Example:
61203  <pre><code>
61204 var reader = new Roo.ReaderLayout();
61205 var CP = Roo.ContentPanel;  // shortcut for adding
61206
61207 reader.beginUpdate();
61208 reader.add("north", new CP("north", "North"));
61209 reader.add("west", new CP("west", {title: "West"}));
61210 reader.add("east", new CP("east", {title: "East"}));
61211
61212 reader.regions.listView.add(new CP("listView", "List"));
61213 reader.regions.preview.add(new CP("preview", "Preview"));
61214 reader.endUpdate();
61215 </code></pre>
61216 * @constructor
61217 * Create a new ReaderLayout
61218 * @param {Object} config Configuration options
61219 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61220 * document.body if omitted)
61221 */
61222 Roo.ReaderLayout = function(config, renderTo){
61223     var c = config || {size:{}};
61224     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61225         north: c.north !== false ? Roo.apply({
61226             split:false,
61227             initialSize: 32,
61228             titlebar: false
61229         }, c.north) : false,
61230         west: c.west !== false ? Roo.apply({
61231             split:true,
61232             initialSize: 200,
61233             minSize: 175,
61234             maxSize: 400,
61235             titlebar: true,
61236             collapsible: true,
61237             animate: true,
61238             margins:{left:5,right:0,bottom:5,top:5},
61239             cmargins:{left:5,right:5,bottom:5,top:5}
61240         }, c.west) : false,
61241         east: c.east !== false ? Roo.apply({
61242             split:true,
61243             initialSize: 200,
61244             minSize: 175,
61245             maxSize: 400,
61246             titlebar: true,
61247             collapsible: true,
61248             animate: true,
61249             margins:{left:0,right:5,bottom:5,top:5},
61250             cmargins:{left:5,right:5,bottom:5,top:5}
61251         }, c.east) : false,
61252         center: Roo.apply({
61253             tabPosition: 'top',
61254             autoScroll:false,
61255             closeOnTab: true,
61256             titlebar:false,
61257             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61258         }, c.center)
61259     });
61260
61261     this.el.addClass('x-reader');
61262
61263     this.beginUpdate();
61264
61265     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61266         south: c.preview !== false ? Roo.apply({
61267             split:true,
61268             initialSize: 200,
61269             minSize: 100,
61270             autoScroll:true,
61271             collapsible:true,
61272             titlebar: true,
61273             cmargins:{top:5,left:0, right:0, bottom:0}
61274         }, c.preview) : false,
61275         center: Roo.apply({
61276             autoScroll:false,
61277             titlebar:false,
61278             minHeight:200
61279         }, c.listView)
61280     });
61281     this.add('center', new Roo.NestedLayoutPanel(inner,
61282             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61283
61284     this.endUpdate();
61285
61286     this.regions.preview = inner.getRegion('south');
61287     this.regions.listView = inner.getRegion('center');
61288 };
61289
61290 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61291  * Based on:
61292  * Ext JS Library 1.1.1
61293  * Copyright(c) 2006-2007, Ext JS, LLC.
61294  *
61295  * Originally Released Under LGPL - original licence link has changed is not relivant.
61296  *
61297  * Fork - LGPL
61298  * <script type="text/javascript">
61299  */
61300  
61301 /**
61302  * @class Roo.grid.Grid
61303  * @extends Roo.util.Observable
61304  * This class represents the primary interface of a component based grid control.
61305  * <br><br>Usage:<pre><code>
61306  var grid = new Roo.grid.Grid("my-container-id", {
61307      ds: myDataStore,
61308      cm: myColModel,
61309      selModel: mySelectionModel,
61310      autoSizeColumns: true,
61311      monitorWindowResize: false,
61312      trackMouseOver: true
61313  });
61314  // set any options
61315  grid.render();
61316  * </code></pre>
61317  * <b>Common Problems:</b><br/>
61318  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61319  * element will correct this<br/>
61320  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61321  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61322  * are unpredictable.<br/>
61323  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61324  * grid to calculate dimensions/offsets.<br/>
61325   * @constructor
61326  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61327  * The container MUST have some type of size defined for the grid to fill. The container will be
61328  * automatically set to position relative if it isn't already.
61329  * @param {Object} config A config object that sets properties on this grid.
61330  */
61331 Roo.grid.Grid = function(container, config){
61332         // initialize the container
61333         this.container = Roo.get(container);
61334         this.container.update("");
61335         this.container.setStyle("overflow", "hidden");
61336     this.container.addClass('x-grid-container');
61337
61338     this.id = this.container.id;
61339
61340     Roo.apply(this, config);
61341     // check and correct shorthanded configs
61342     if(this.ds){
61343         this.dataSource = this.ds;
61344         delete this.ds;
61345     }
61346     if(this.cm){
61347         this.colModel = this.cm;
61348         delete this.cm;
61349     }
61350     if(this.sm){
61351         this.selModel = this.sm;
61352         delete this.sm;
61353     }
61354
61355     if (this.selModel) {
61356         this.selModel = Roo.factory(this.selModel, Roo.grid);
61357         this.sm = this.selModel;
61358         this.sm.xmodule = this.xmodule || false;
61359     }
61360     if (typeof(this.colModel.config) == 'undefined') {
61361         this.colModel = new Roo.grid.ColumnModel(this.colModel);
61362         this.cm = this.colModel;
61363         this.cm.xmodule = this.xmodule || false;
61364     }
61365     if (this.dataSource) {
61366         this.dataSource= Roo.factory(this.dataSource, Roo.data);
61367         this.ds = this.dataSource;
61368         this.ds.xmodule = this.xmodule || false;
61369          
61370     }
61371     
61372     
61373     
61374     if(this.width){
61375         this.container.setWidth(this.width);
61376     }
61377
61378     if(this.height){
61379         this.container.setHeight(this.height);
61380     }
61381     /** @private */
61382         this.addEvents({
61383         // raw events
61384         /**
61385          * @event click
61386          * The raw click event for the entire grid.
61387          * @param {Roo.EventObject} e
61388          */
61389         "click" : true,
61390         /**
61391          * @event dblclick
61392          * The raw dblclick event for the entire grid.
61393          * @param {Roo.EventObject} e
61394          */
61395         "dblclick" : true,
61396         /**
61397          * @event contextmenu
61398          * The raw contextmenu event for the entire grid.
61399          * @param {Roo.EventObject} e
61400          */
61401         "contextmenu" : true,
61402         /**
61403          * @event mousedown
61404          * The raw mousedown event for the entire grid.
61405          * @param {Roo.EventObject} e
61406          */
61407         "mousedown" : true,
61408         /**
61409          * @event mouseup
61410          * The raw mouseup event for the entire grid.
61411          * @param {Roo.EventObject} e
61412          */
61413         "mouseup" : true,
61414         /**
61415          * @event mouseover
61416          * The raw mouseover event for the entire grid.
61417          * @param {Roo.EventObject} e
61418          */
61419         "mouseover" : true,
61420         /**
61421          * @event mouseout
61422          * The raw mouseout event for the entire grid.
61423          * @param {Roo.EventObject} e
61424          */
61425         "mouseout" : true,
61426         /**
61427          * @event keypress
61428          * The raw keypress event for the entire grid.
61429          * @param {Roo.EventObject} e
61430          */
61431         "keypress" : true,
61432         /**
61433          * @event keydown
61434          * The raw keydown event for the entire grid.
61435          * @param {Roo.EventObject} e
61436          */
61437         "keydown" : true,
61438
61439         // custom events
61440
61441         /**
61442          * @event cellclick
61443          * Fires when a cell is clicked
61444          * @param {Grid} this
61445          * @param {Number} rowIndex
61446          * @param {Number} columnIndex
61447          * @param {Roo.EventObject} e
61448          */
61449         "cellclick" : true,
61450         /**
61451          * @event celldblclick
61452          * Fires when a cell is double clicked
61453          * @param {Grid} this
61454          * @param {Number} rowIndex
61455          * @param {Number} columnIndex
61456          * @param {Roo.EventObject} e
61457          */
61458         "celldblclick" : true,
61459         /**
61460          * @event rowclick
61461          * Fires when a row is clicked
61462          * @param {Grid} this
61463          * @param {Number} rowIndex
61464          * @param {Roo.EventObject} e
61465          */
61466         "rowclick" : true,
61467         /**
61468          * @event rowdblclick
61469          * Fires when a row is double clicked
61470          * @param {Grid} this
61471          * @param {Number} rowIndex
61472          * @param {Roo.EventObject} e
61473          */
61474         "rowdblclick" : true,
61475         /**
61476          * @event headerclick
61477          * Fires when a header is clicked
61478          * @param {Grid} this
61479          * @param {Number} columnIndex
61480          * @param {Roo.EventObject} e
61481          */
61482         "headerclick" : true,
61483         /**
61484          * @event headerdblclick
61485          * Fires when a header cell is double clicked
61486          * @param {Grid} this
61487          * @param {Number} columnIndex
61488          * @param {Roo.EventObject} e
61489          */
61490         "headerdblclick" : true,
61491         /**
61492          * @event rowcontextmenu
61493          * Fires when a row is right clicked
61494          * @param {Grid} this
61495          * @param {Number} rowIndex
61496          * @param {Roo.EventObject} e
61497          */
61498         "rowcontextmenu" : true,
61499         /**
61500          * @event cellcontextmenu
61501          * Fires when a cell is right clicked
61502          * @param {Grid} this
61503          * @param {Number} rowIndex
61504          * @param {Number} cellIndex
61505          * @param {Roo.EventObject} e
61506          */
61507          "cellcontextmenu" : true,
61508         /**
61509          * @event headercontextmenu
61510          * Fires when a header is right clicked
61511          * @param {Grid} this
61512          * @param {Number} columnIndex
61513          * @param {Roo.EventObject} e
61514          */
61515         "headercontextmenu" : true,
61516         /**
61517          * @event bodyscroll
61518          * Fires when the body element is scrolled
61519          * @param {Number} scrollLeft
61520          * @param {Number} scrollTop
61521          */
61522         "bodyscroll" : true,
61523         /**
61524          * @event columnresize
61525          * Fires when the user resizes a column
61526          * @param {Number} columnIndex
61527          * @param {Number} newSize
61528          */
61529         "columnresize" : true,
61530         /**
61531          * @event columnmove
61532          * Fires when the user moves a column
61533          * @param {Number} oldIndex
61534          * @param {Number} newIndex
61535          */
61536         "columnmove" : true,
61537         /**
61538          * @event startdrag
61539          * Fires when row(s) start being dragged
61540          * @param {Grid} this
61541          * @param {Roo.GridDD} dd The drag drop object
61542          * @param {event} e The raw browser event
61543          */
61544         "startdrag" : true,
61545         /**
61546          * @event enddrag
61547          * Fires when a drag operation is complete
61548          * @param {Grid} this
61549          * @param {Roo.GridDD} dd The drag drop object
61550          * @param {event} e The raw browser event
61551          */
61552         "enddrag" : true,
61553         /**
61554          * @event dragdrop
61555          * Fires when dragged row(s) are dropped on a valid DD target
61556          * @param {Grid} this
61557          * @param {Roo.GridDD} dd The drag drop object
61558          * @param {String} targetId The target drag drop object
61559          * @param {event} e The raw browser event
61560          */
61561         "dragdrop" : true,
61562         /**
61563          * @event dragover
61564          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61565          * @param {Grid} this
61566          * @param {Roo.GridDD} dd The drag drop object
61567          * @param {String} targetId The target drag drop object
61568          * @param {event} e The raw browser event
61569          */
61570         "dragover" : true,
61571         /**
61572          * @event dragenter
61573          *  Fires when the dragged row(s) first cross another DD target while being dragged
61574          * @param {Grid} this
61575          * @param {Roo.GridDD} dd The drag drop object
61576          * @param {String} targetId The target drag drop object
61577          * @param {event} e The raw browser event
61578          */
61579         "dragenter" : true,
61580         /**
61581          * @event dragout
61582          * Fires when the dragged row(s) leave another DD target while being dragged
61583          * @param {Grid} this
61584          * @param {Roo.GridDD} dd The drag drop object
61585          * @param {String} targetId The target drag drop object
61586          * @param {event} e The raw browser event
61587          */
61588         "dragout" : true,
61589         /**
61590          * @event rowclass
61591          * Fires when a row is rendered, so you can change add a style to it.
61592          * @param {GridView} gridview   The grid view
61593          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61594          */
61595         'rowclass' : true,
61596
61597         /**
61598          * @event render
61599          * Fires when the grid is rendered
61600          * @param {Grid} grid
61601          */
61602         'render' : true
61603     });
61604
61605     Roo.grid.Grid.superclass.constructor.call(this);
61606 };
61607 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61608     
61609     /**
61610          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61611          */
61612         /**
61613          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
61614          */
61615         /**
61616          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61617          */
61618         /**
61619          * @cfg {Roo.data.Store} ds The data store for the grid
61620          */
61621         /**
61622          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61623          */
61624         /**
61625      * @cfg {String} ddGroup - drag drop group.
61626      */
61627       /**
61628      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61629      */
61630
61631     /**
61632      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61633      */
61634     minColumnWidth : 25,
61635
61636     /**
61637      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61638      * <b>on initial render.</b> It is more efficient to explicitly size the columns
61639      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
61640      */
61641     autoSizeColumns : false,
61642
61643     /**
61644      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61645      */
61646     autoSizeHeaders : true,
61647
61648     /**
61649      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61650      */
61651     monitorWindowResize : true,
61652
61653     /**
61654      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61655      * rows measured to get a columns size. Default is 0 (all rows).
61656      */
61657     maxRowsToMeasure : 0,
61658
61659     /**
61660      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61661      */
61662     trackMouseOver : true,
61663
61664     /**
61665     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
61666     */
61667       /**
61668     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
61669     */
61670     
61671     /**
61672     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61673     */
61674     enableDragDrop : false,
61675     
61676     /**
61677     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61678     */
61679     enableColumnMove : true,
61680     
61681     /**
61682     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61683     */
61684     enableColumnHide : true,
61685     
61686     /**
61687     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61688     */
61689     enableRowHeightSync : false,
61690     
61691     /**
61692     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
61693     */
61694     stripeRows : true,
61695     
61696     /**
61697     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61698     */
61699     autoHeight : false,
61700
61701     /**
61702      * @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.
61703      */
61704     autoExpandColumn : false,
61705
61706     /**
61707     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61708     * Default is 50.
61709     */
61710     autoExpandMin : 50,
61711
61712     /**
61713     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61714     */
61715     autoExpandMax : 1000,
61716
61717     /**
61718     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61719     */
61720     view : null,
61721
61722     /**
61723     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61724     */
61725     loadMask : false,
61726     /**
61727     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61728     */
61729     dropTarget: false,
61730      /**
61731     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
61732     */ 
61733     sortColMenu : false,
61734     
61735     // private
61736     rendered : false,
61737
61738     /**
61739     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
61740     * of a fixed width. Default is false.
61741     */
61742     /**
61743     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
61744     */
61745     
61746     
61747     /**
61748     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
61749     * %0 is replaced with the number of selected rows.
61750     */
61751     ddText : "{0} selected row{1}",
61752     
61753     
61754     /**
61755      * Called once after all setup has been completed and the grid is ready to be rendered.
61756      * @return {Roo.grid.Grid} this
61757      */
61758     render : function()
61759     {
61760         var c = this.container;
61761         // try to detect autoHeight/width mode
61762         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
61763             this.autoHeight = true;
61764         }
61765         var view = this.getView();
61766         view.init(this);
61767
61768         c.on("click", this.onClick, this);
61769         c.on("dblclick", this.onDblClick, this);
61770         c.on("contextmenu", this.onContextMenu, this);
61771         c.on("keydown", this.onKeyDown, this);
61772         if (Roo.isTouch) {
61773             c.on("touchstart", this.onTouchStart, this);
61774         }
61775
61776         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
61777
61778         this.getSelectionModel().init(this);
61779
61780         view.render();
61781
61782         if(this.loadMask){
61783             this.loadMask = new Roo.LoadMask(this.container,
61784                     Roo.apply({store:this.dataSource}, this.loadMask));
61785         }
61786         
61787         
61788         if (this.toolbar && this.toolbar.xtype) {
61789             this.toolbar.container = this.getView().getHeaderPanel(true);
61790             this.toolbar = new Roo.Toolbar(this.toolbar);
61791         }
61792         if (this.footer && this.footer.xtype) {
61793             this.footer.dataSource = this.getDataSource();
61794             this.footer.container = this.getView().getFooterPanel(true);
61795             this.footer = Roo.factory(this.footer, Roo);
61796         }
61797         if (this.dropTarget && this.dropTarget.xtype) {
61798             delete this.dropTarget.xtype;
61799             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
61800         }
61801         
61802         
61803         this.rendered = true;
61804         this.fireEvent('render', this);
61805         return this;
61806     },
61807
61808     /**
61809      * Reconfigures the grid to use a different Store and Column Model.
61810      * The View will be bound to the new objects and refreshed.
61811      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
61812      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
61813      */
61814     reconfigure : function(dataSource, colModel){
61815         if(this.loadMask){
61816             this.loadMask.destroy();
61817             this.loadMask = new Roo.LoadMask(this.container,
61818                     Roo.apply({store:dataSource}, this.loadMask));
61819         }
61820         this.view.bind(dataSource, colModel);
61821         this.dataSource = dataSource;
61822         this.colModel = colModel;
61823         this.view.refresh(true);
61824     },
61825     /**
61826      * addColumns
61827      * Add's a column, default at the end..
61828      
61829      * @param {int} position to add (default end)
61830      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
61831      */
61832     addColumns : function(pos, ar)
61833     {
61834         
61835         for (var i =0;i< ar.length;i++) {
61836             var cfg = ar[i];
61837             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
61838             this.cm.lookup[cfg.id] = cfg;
61839         }
61840         
61841         
61842         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
61843             pos = this.cm.config.length; //this.cm.config.push(cfg);
61844         } 
61845         pos = Math.max(0,pos);
61846         ar.unshift(0);
61847         ar.unshift(pos);
61848         this.cm.config.splice.apply(this.cm.config, ar);
61849         
61850         
61851         
61852         this.view.generateRules(this.cm);
61853         this.view.refresh(true);
61854         
61855     },
61856     
61857     
61858     
61859     
61860     // private
61861     onKeyDown : function(e){
61862         this.fireEvent("keydown", e);
61863     },
61864
61865     /**
61866      * Destroy this grid.
61867      * @param {Boolean} removeEl True to remove the element
61868      */
61869     destroy : function(removeEl, keepListeners){
61870         if(this.loadMask){
61871             this.loadMask.destroy();
61872         }
61873         var c = this.container;
61874         c.removeAllListeners();
61875         this.view.destroy();
61876         this.colModel.purgeListeners();
61877         if(!keepListeners){
61878             this.purgeListeners();
61879         }
61880         c.update("");
61881         if(removeEl === true){
61882             c.remove();
61883         }
61884     },
61885
61886     // private
61887     processEvent : function(name, e){
61888         // does this fire select???
61889         //Roo.log('grid:processEvent '  + name);
61890         
61891         if (name != 'touchstart' ) {
61892             this.fireEvent(name, e);    
61893         }
61894         
61895         var t = e.getTarget();
61896         var v = this.view;
61897         var header = v.findHeaderIndex(t);
61898         if(header !== false){
61899             var ename = name == 'touchstart' ? 'click' : name;
61900              
61901             this.fireEvent("header" + ename, this, header, e);
61902         }else{
61903             var row = v.findRowIndex(t);
61904             var cell = v.findCellIndex(t);
61905             if (name == 'touchstart') {
61906                 // first touch is always a click.
61907                 // hopefull this happens after selection is updated.?
61908                 name = false;
61909                 
61910                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
61911                     var cs = this.selModel.getSelectedCell();
61912                     if (row == cs[0] && cell == cs[1]){
61913                         name = 'dblclick';
61914                     }
61915                 }
61916                 if (typeof(this.selModel.getSelections) != 'undefined') {
61917                     var cs = this.selModel.getSelections();
61918                     var ds = this.dataSource;
61919                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
61920                         name = 'dblclick';
61921                     }
61922                 }
61923                 if (!name) {
61924                     return;
61925                 }
61926             }
61927             
61928             
61929             if(row !== false){
61930                 this.fireEvent("row" + name, this, row, e);
61931                 if(cell !== false){
61932                     this.fireEvent("cell" + name, this, row, cell, e);
61933                 }
61934             }
61935         }
61936     },
61937
61938     // private
61939     onClick : function(e){
61940         this.processEvent("click", e);
61941     },
61942    // private
61943     onTouchStart : function(e){
61944         this.processEvent("touchstart", e);
61945     },
61946
61947     // private
61948     onContextMenu : function(e, t){
61949         this.processEvent("contextmenu", e);
61950     },
61951
61952     // private
61953     onDblClick : function(e){
61954         this.processEvent("dblclick", e);
61955     },
61956
61957     // private
61958     walkCells : function(row, col, step, fn, scope){
61959         var cm = this.colModel, clen = cm.getColumnCount();
61960         var ds = this.dataSource, rlen = ds.getCount(), first = true;
61961         if(step < 0){
61962             if(col < 0){
61963                 row--;
61964                 first = false;
61965             }
61966             while(row >= 0){
61967                 if(!first){
61968                     col = clen-1;
61969                 }
61970                 first = false;
61971                 while(col >= 0){
61972                     if(fn.call(scope || this, row, col, cm) === true){
61973                         return [row, col];
61974                     }
61975                     col--;
61976                 }
61977                 row--;
61978             }
61979         } else {
61980             if(col >= clen){
61981                 row++;
61982                 first = false;
61983             }
61984             while(row < rlen){
61985                 if(!first){
61986                     col = 0;
61987                 }
61988                 first = false;
61989                 while(col < clen){
61990                     if(fn.call(scope || this, row, col, cm) === true){
61991                         return [row, col];
61992                     }
61993                     col++;
61994                 }
61995                 row++;
61996             }
61997         }
61998         return null;
61999     },
62000
62001     // private
62002     getSelections : function(){
62003         return this.selModel.getSelections();
62004     },
62005
62006     /**
62007      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62008      * but if manual update is required this method will initiate it.
62009      */
62010     autoSize : function(){
62011         if(this.rendered){
62012             this.view.layout();
62013             if(this.view.adjustForScroll){
62014                 this.view.adjustForScroll();
62015             }
62016         }
62017     },
62018
62019     /**
62020      * Returns the grid's underlying element.
62021      * @return {Element} The element
62022      */
62023     getGridEl : function(){
62024         return this.container;
62025     },
62026
62027     // private for compatibility, overridden by editor grid
62028     stopEditing : function(){},
62029
62030     /**
62031      * Returns the grid's SelectionModel.
62032      * @return {SelectionModel}
62033      */
62034     getSelectionModel : function(){
62035         if(!this.selModel){
62036             this.selModel = new Roo.grid.RowSelectionModel();
62037         }
62038         return this.selModel;
62039     },
62040
62041     /**
62042      * Returns the grid's DataSource.
62043      * @return {DataSource}
62044      */
62045     getDataSource : function(){
62046         return this.dataSource;
62047     },
62048
62049     /**
62050      * Returns the grid's ColumnModel.
62051      * @return {ColumnModel}
62052      */
62053     getColumnModel : function(){
62054         return this.colModel;
62055     },
62056
62057     /**
62058      * Returns the grid's GridView object.
62059      * @return {GridView}
62060      */
62061     getView : function(){
62062         if(!this.view){
62063             this.view = new Roo.grid.GridView(this.viewConfig);
62064             this.relayEvents(this.view, [
62065                 "beforerowremoved", "beforerowsinserted",
62066                 "beforerefresh", "rowremoved",
62067                 "rowsinserted", "rowupdated" ,"refresh"
62068             ]);
62069         }
62070         return this.view;
62071     },
62072     /**
62073      * Called to get grid's drag proxy text, by default returns this.ddText.
62074      * Override this to put something different in the dragged text.
62075      * @return {String}
62076      */
62077     getDragDropText : function(){
62078         var count = this.selModel.getCount();
62079         return String.format(this.ddText, count, count == 1 ? '' : 's');
62080     }
62081 });
62082 /*
62083  * Based on:
62084  * Ext JS Library 1.1.1
62085  * Copyright(c) 2006-2007, Ext JS, LLC.
62086  *
62087  * Originally Released Under LGPL - original licence link has changed is not relivant.
62088  *
62089  * Fork - LGPL
62090  * <script type="text/javascript">
62091  */
62092  /**
62093  * @class Roo.grid.AbstractGridView
62094  * @extends Roo.util.Observable
62095  * @abstract
62096  * Abstract base class for grid Views
62097  * @constructor
62098  */
62099 Roo.grid.AbstractGridView = function(){
62100         this.grid = null;
62101         
62102         this.events = {
62103             "beforerowremoved" : true,
62104             "beforerowsinserted" : true,
62105             "beforerefresh" : true,
62106             "rowremoved" : true,
62107             "rowsinserted" : true,
62108             "rowupdated" : true,
62109             "refresh" : true
62110         };
62111     Roo.grid.AbstractGridView.superclass.constructor.call(this);
62112 };
62113
62114 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62115     rowClass : "x-grid-row",
62116     cellClass : "x-grid-cell",
62117     tdClass : "x-grid-td",
62118     hdClass : "x-grid-hd",
62119     splitClass : "x-grid-hd-split",
62120     
62121     init: function(grid){
62122         this.grid = grid;
62123                 var cid = this.grid.getGridEl().id;
62124         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62125         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62126         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62127         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62128         },
62129         
62130     getColumnRenderers : function(){
62131         var renderers = [];
62132         var cm = this.grid.colModel;
62133         var colCount = cm.getColumnCount();
62134         for(var i = 0; i < colCount; i++){
62135             renderers[i] = cm.getRenderer(i);
62136         }
62137         return renderers;
62138     },
62139     
62140     getColumnIds : function(){
62141         var ids = [];
62142         var cm = this.grid.colModel;
62143         var colCount = cm.getColumnCount();
62144         for(var i = 0; i < colCount; i++){
62145             ids[i] = cm.getColumnId(i);
62146         }
62147         return ids;
62148     },
62149     
62150     getDataIndexes : function(){
62151         if(!this.indexMap){
62152             this.indexMap = this.buildIndexMap();
62153         }
62154         return this.indexMap.colToData;
62155     },
62156     
62157     getColumnIndexByDataIndex : function(dataIndex){
62158         if(!this.indexMap){
62159             this.indexMap = this.buildIndexMap();
62160         }
62161         return this.indexMap.dataToCol[dataIndex];
62162     },
62163     
62164     /**
62165      * Set a css style for a column dynamically. 
62166      * @param {Number} colIndex The index of the column
62167      * @param {String} name The css property name
62168      * @param {String} value The css value
62169      */
62170     setCSSStyle : function(colIndex, name, value){
62171         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62172         Roo.util.CSS.updateRule(selector, name, value);
62173     },
62174     
62175     generateRules : function(cm){
62176         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62177         Roo.util.CSS.removeStyleSheet(rulesId);
62178         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62179             var cid = cm.getColumnId(i);
62180             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62181                          this.tdSelector, cid, " {\n}\n",
62182                          this.hdSelector, cid, " {\n}\n",
62183                          this.splitSelector, cid, " {\n}\n");
62184         }
62185         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62186     }
62187 });/*
62188  * Based on:
62189  * Ext JS Library 1.1.1
62190  * Copyright(c) 2006-2007, Ext JS, LLC.
62191  *
62192  * Originally Released Under LGPL - original licence link has changed is not relivant.
62193  *
62194  * Fork - LGPL
62195  * <script type="text/javascript">
62196  */
62197
62198 // private
62199 // This is a support class used internally by the Grid components
62200 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62201     this.grid = grid;
62202     this.view = grid.getView();
62203     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62204     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62205     if(hd2){
62206         this.setHandleElId(Roo.id(hd));
62207         this.setOuterHandleElId(Roo.id(hd2));
62208     }
62209     this.scroll = false;
62210 };
62211 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62212     maxDragWidth: 120,
62213     getDragData : function(e){
62214         var t = Roo.lib.Event.getTarget(e);
62215         var h = this.view.findHeaderCell(t);
62216         if(h){
62217             return {ddel: h.firstChild, header:h};
62218         }
62219         return false;
62220     },
62221
62222     onInitDrag : function(e){
62223         this.view.headersDisabled = true;
62224         var clone = this.dragData.ddel.cloneNode(true);
62225         clone.id = Roo.id();
62226         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62227         this.proxy.update(clone);
62228         return true;
62229     },
62230
62231     afterValidDrop : function(){
62232         var v = this.view;
62233         setTimeout(function(){
62234             v.headersDisabled = false;
62235         }, 50);
62236     },
62237
62238     afterInvalidDrop : function(){
62239         var v = this.view;
62240         setTimeout(function(){
62241             v.headersDisabled = false;
62242         }, 50);
62243     }
62244 });
62245 /*
62246  * Based on:
62247  * Ext JS Library 1.1.1
62248  * Copyright(c) 2006-2007, Ext JS, LLC.
62249  *
62250  * Originally Released Under LGPL - original licence link has changed is not relivant.
62251  *
62252  * Fork - LGPL
62253  * <script type="text/javascript">
62254  */
62255 // private
62256 // This is a support class used internally by the Grid components
62257 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62258     this.grid = grid;
62259     this.view = grid.getView();
62260     // split the proxies so they don't interfere with mouse events
62261     this.proxyTop = Roo.DomHelper.append(document.body, {
62262         cls:"col-move-top", html:"&#160;"
62263     }, true);
62264     this.proxyBottom = Roo.DomHelper.append(document.body, {
62265         cls:"col-move-bottom", html:"&#160;"
62266     }, true);
62267     this.proxyTop.hide = this.proxyBottom.hide = function(){
62268         this.setLeftTop(-100,-100);
62269         this.setStyle("visibility", "hidden");
62270     };
62271     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62272     // temporarily disabled
62273     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62274     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62275 };
62276 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62277     proxyOffsets : [-4, -9],
62278     fly: Roo.Element.fly,
62279
62280     getTargetFromEvent : function(e){
62281         var t = Roo.lib.Event.getTarget(e);
62282         var cindex = this.view.findCellIndex(t);
62283         if(cindex !== false){
62284             return this.view.getHeaderCell(cindex);
62285         }
62286         return null;
62287     },
62288
62289     nextVisible : function(h){
62290         var v = this.view, cm = this.grid.colModel;
62291         h = h.nextSibling;
62292         while(h){
62293             if(!cm.isHidden(v.getCellIndex(h))){
62294                 return h;
62295             }
62296             h = h.nextSibling;
62297         }
62298         return null;
62299     },
62300
62301     prevVisible : function(h){
62302         var v = this.view, cm = this.grid.colModel;
62303         h = h.prevSibling;
62304         while(h){
62305             if(!cm.isHidden(v.getCellIndex(h))){
62306                 return h;
62307             }
62308             h = h.prevSibling;
62309         }
62310         return null;
62311     },
62312
62313     positionIndicator : function(h, n, e){
62314         var x = Roo.lib.Event.getPageX(e);
62315         var r = Roo.lib.Dom.getRegion(n.firstChild);
62316         var px, pt, py = r.top + this.proxyOffsets[1];
62317         if((r.right - x) <= (r.right-r.left)/2){
62318             px = r.right+this.view.borderWidth;
62319             pt = "after";
62320         }else{
62321             px = r.left;
62322             pt = "before";
62323         }
62324         var oldIndex = this.view.getCellIndex(h);
62325         var newIndex = this.view.getCellIndex(n);
62326
62327         if(this.grid.colModel.isFixed(newIndex)){
62328             return false;
62329         }
62330
62331         var locked = this.grid.colModel.isLocked(newIndex);
62332
62333         if(pt == "after"){
62334             newIndex++;
62335         }
62336         if(oldIndex < newIndex){
62337             newIndex--;
62338         }
62339         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62340             return false;
62341         }
62342         px +=  this.proxyOffsets[0];
62343         this.proxyTop.setLeftTop(px, py);
62344         this.proxyTop.show();
62345         if(!this.bottomOffset){
62346             this.bottomOffset = this.view.mainHd.getHeight();
62347         }
62348         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62349         this.proxyBottom.show();
62350         return pt;
62351     },
62352
62353     onNodeEnter : function(n, dd, e, data){
62354         if(data.header != n){
62355             this.positionIndicator(data.header, n, e);
62356         }
62357     },
62358
62359     onNodeOver : function(n, dd, e, data){
62360         var result = false;
62361         if(data.header != n){
62362             result = this.positionIndicator(data.header, n, e);
62363         }
62364         if(!result){
62365             this.proxyTop.hide();
62366             this.proxyBottom.hide();
62367         }
62368         return result ? this.dropAllowed : this.dropNotAllowed;
62369     },
62370
62371     onNodeOut : function(n, dd, e, data){
62372         this.proxyTop.hide();
62373         this.proxyBottom.hide();
62374     },
62375
62376     onNodeDrop : function(n, dd, e, data){
62377         var h = data.header;
62378         if(h != n){
62379             var cm = this.grid.colModel;
62380             var x = Roo.lib.Event.getPageX(e);
62381             var r = Roo.lib.Dom.getRegion(n.firstChild);
62382             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62383             var oldIndex = this.view.getCellIndex(h);
62384             var newIndex = this.view.getCellIndex(n);
62385             var locked = cm.isLocked(newIndex);
62386             if(pt == "after"){
62387                 newIndex++;
62388             }
62389             if(oldIndex < newIndex){
62390                 newIndex--;
62391             }
62392             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62393                 return false;
62394             }
62395             cm.setLocked(oldIndex, locked, true);
62396             cm.moveColumn(oldIndex, newIndex);
62397             this.grid.fireEvent("columnmove", oldIndex, newIndex);
62398             return true;
62399         }
62400         return false;
62401     }
62402 });
62403 /*
62404  * Based on:
62405  * Ext JS Library 1.1.1
62406  * Copyright(c) 2006-2007, Ext JS, LLC.
62407  *
62408  * Originally Released Under LGPL - original licence link has changed is not relivant.
62409  *
62410  * Fork - LGPL
62411  * <script type="text/javascript">
62412  */
62413   
62414 /**
62415  * @class Roo.grid.GridView
62416  * @extends Roo.util.Observable
62417  *
62418  * @constructor
62419  * @param {Object} config
62420  */
62421 Roo.grid.GridView = function(config){
62422     Roo.grid.GridView.superclass.constructor.call(this);
62423     this.el = null;
62424
62425     Roo.apply(this, config);
62426 };
62427
62428 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62429
62430     unselectable :  'unselectable="on"',
62431     unselectableCls :  'x-unselectable',
62432     
62433     
62434     rowClass : "x-grid-row",
62435
62436     cellClass : "x-grid-col",
62437
62438     tdClass : "x-grid-td",
62439
62440     hdClass : "x-grid-hd",
62441
62442     splitClass : "x-grid-split",
62443
62444     sortClasses : ["sort-asc", "sort-desc"],
62445
62446     enableMoveAnim : false,
62447
62448     hlColor: "C3DAF9",
62449
62450     dh : Roo.DomHelper,
62451
62452     fly : Roo.Element.fly,
62453
62454     css : Roo.util.CSS,
62455
62456     borderWidth: 1,
62457
62458     splitOffset: 3,
62459
62460     scrollIncrement : 22,
62461
62462     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62463
62464     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62465
62466     bind : function(ds, cm){
62467         if(this.ds){
62468             this.ds.un("load", this.onLoad, this);
62469             this.ds.un("datachanged", this.onDataChange, this);
62470             this.ds.un("add", this.onAdd, this);
62471             this.ds.un("remove", this.onRemove, this);
62472             this.ds.un("update", this.onUpdate, this);
62473             this.ds.un("clear", this.onClear, this);
62474         }
62475         if(ds){
62476             ds.on("load", this.onLoad, this);
62477             ds.on("datachanged", this.onDataChange, this);
62478             ds.on("add", this.onAdd, this);
62479             ds.on("remove", this.onRemove, this);
62480             ds.on("update", this.onUpdate, this);
62481             ds.on("clear", this.onClear, this);
62482         }
62483         this.ds = ds;
62484
62485         if(this.cm){
62486             this.cm.un("widthchange", this.onColWidthChange, this);
62487             this.cm.un("headerchange", this.onHeaderChange, this);
62488             this.cm.un("hiddenchange", this.onHiddenChange, this);
62489             this.cm.un("columnmoved", this.onColumnMove, this);
62490             this.cm.un("columnlockchange", this.onColumnLock, this);
62491         }
62492         if(cm){
62493             this.generateRules(cm);
62494             cm.on("widthchange", this.onColWidthChange, this);
62495             cm.on("headerchange", this.onHeaderChange, this);
62496             cm.on("hiddenchange", this.onHiddenChange, this);
62497             cm.on("columnmoved", this.onColumnMove, this);
62498             cm.on("columnlockchange", this.onColumnLock, this);
62499         }
62500         this.cm = cm;
62501     },
62502
62503     init: function(grid){
62504         Roo.grid.GridView.superclass.init.call(this, grid);
62505
62506         this.bind(grid.dataSource, grid.colModel);
62507
62508         grid.on("headerclick", this.handleHeaderClick, this);
62509
62510         if(grid.trackMouseOver){
62511             grid.on("mouseover", this.onRowOver, this);
62512             grid.on("mouseout", this.onRowOut, this);
62513         }
62514         grid.cancelTextSelection = function(){};
62515         this.gridId = grid.id;
62516
62517         var tpls = this.templates || {};
62518
62519         if(!tpls.master){
62520             tpls.master = new Roo.Template(
62521                '<div class="x-grid" hidefocus="true">',
62522                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62523                   '<div class="x-grid-topbar"></div>',
62524                   '<div class="x-grid-scroller"><div></div></div>',
62525                   '<div class="x-grid-locked">',
62526                       '<div class="x-grid-header">{lockedHeader}</div>',
62527                       '<div class="x-grid-body">{lockedBody}</div>',
62528                   "</div>",
62529                   '<div class="x-grid-viewport">',
62530                       '<div class="x-grid-header">{header}</div>',
62531                       '<div class="x-grid-body">{body}</div>',
62532                   "</div>",
62533                   '<div class="x-grid-bottombar"></div>',
62534                  
62535                   '<div class="x-grid-resize-proxy">&#160;</div>',
62536                "</div>"
62537             );
62538             tpls.master.disableformats = true;
62539         }
62540
62541         if(!tpls.header){
62542             tpls.header = new Roo.Template(
62543                '<table border="0" cellspacing="0" cellpadding="0">',
62544                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62545                "</table>{splits}"
62546             );
62547             tpls.header.disableformats = true;
62548         }
62549         tpls.header.compile();
62550
62551         if(!tpls.hcell){
62552             tpls.hcell = new Roo.Template(
62553                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62554                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62555                 "</div></td>"
62556              );
62557              tpls.hcell.disableFormats = true;
62558         }
62559         tpls.hcell.compile();
62560
62561         if(!tpls.hsplit){
62562             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62563                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
62564             tpls.hsplit.disableFormats = true;
62565         }
62566         tpls.hsplit.compile();
62567
62568         if(!tpls.body){
62569             tpls.body = new Roo.Template(
62570                '<table border="0" cellspacing="0" cellpadding="0">',
62571                "<tbody>{rows}</tbody>",
62572                "</table>"
62573             );
62574             tpls.body.disableFormats = true;
62575         }
62576         tpls.body.compile();
62577
62578         if(!tpls.row){
62579             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62580             tpls.row.disableFormats = true;
62581         }
62582         tpls.row.compile();
62583
62584         if(!tpls.cell){
62585             tpls.cell = new Roo.Template(
62586                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62587                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62588                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62589                 "</td>"
62590             );
62591             tpls.cell.disableFormats = true;
62592         }
62593         tpls.cell.compile();
62594
62595         this.templates = tpls;
62596     },
62597
62598     // remap these for backwards compat
62599     onColWidthChange : function(){
62600         this.updateColumns.apply(this, arguments);
62601     },
62602     onHeaderChange : function(){
62603         this.updateHeaders.apply(this, arguments);
62604     }, 
62605     onHiddenChange : function(){
62606         this.handleHiddenChange.apply(this, arguments);
62607     },
62608     onColumnMove : function(){
62609         this.handleColumnMove.apply(this, arguments);
62610     },
62611     onColumnLock : function(){
62612         this.handleLockChange.apply(this, arguments);
62613     },
62614
62615     onDataChange : function(){
62616         this.refresh();
62617         this.updateHeaderSortState();
62618     },
62619
62620     onClear : function(){
62621         this.refresh();
62622     },
62623
62624     onUpdate : function(ds, record){
62625         this.refreshRow(record);
62626     },
62627
62628     refreshRow : function(record){
62629         var ds = this.ds, index;
62630         if(typeof record == 'number'){
62631             index = record;
62632             record = ds.getAt(index);
62633         }else{
62634             index = ds.indexOf(record);
62635         }
62636         this.insertRows(ds, index, index, true);
62637         this.onRemove(ds, record, index+1, true);
62638         this.syncRowHeights(index, index);
62639         this.layout();
62640         this.fireEvent("rowupdated", this, index, record);
62641     },
62642
62643     onAdd : function(ds, records, index){
62644         this.insertRows(ds, index, index + (records.length-1));
62645     },
62646
62647     onRemove : function(ds, record, index, isUpdate){
62648         if(isUpdate !== true){
62649             this.fireEvent("beforerowremoved", this, index, record);
62650         }
62651         var bt = this.getBodyTable(), lt = this.getLockedTable();
62652         if(bt.rows[index]){
62653             bt.firstChild.removeChild(bt.rows[index]);
62654         }
62655         if(lt.rows[index]){
62656             lt.firstChild.removeChild(lt.rows[index]);
62657         }
62658         if(isUpdate !== true){
62659             this.stripeRows(index);
62660             this.syncRowHeights(index, index);
62661             this.layout();
62662             this.fireEvent("rowremoved", this, index, record);
62663         }
62664     },
62665
62666     onLoad : function(){
62667         this.scrollToTop();
62668     },
62669
62670     /**
62671      * Scrolls the grid to the top
62672      */
62673     scrollToTop : function(){
62674         if(this.scroller){
62675             this.scroller.dom.scrollTop = 0;
62676             this.syncScroll();
62677         }
62678     },
62679
62680     /**
62681      * Gets a panel in the header of the grid that can be used for toolbars etc.
62682      * After modifying the contents of this panel a call to grid.autoSize() may be
62683      * required to register any changes in size.
62684      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62685      * @return Roo.Element
62686      */
62687     getHeaderPanel : function(doShow){
62688         if(doShow){
62689             this.headerPanel.show();
62690         }
62691         return this.headerPanel;
62692     },
62693
62694     /**
62695      * Gets a panel in the footer of the grid that can be used for toolbars etc.
62696      * After modifying the contents of this panel a call to grid.autoSize() may be
62697      * required to register any changes in size.
62698      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62699      * @return Roo.Element
62700      */
62701     getFooterPanel : function(doShow){
62702         if(doShow){
62703             this.footerPanel.show();
62704         }
62705         return this.footerPanel;
62706     },
62707
62708     initElements : function(){
62709         var E = Roo.Element;
62710         var el = this.grid.getGridEl().dom.firstChild;
62711         var cs = el.childNodes;
62712
62713         this.el = new E(el);
62714         
62715          this.focusEl = new E(el.firstChild);
62716         this.focusEl.swallowEvent("click", true);
62717         
62718         this.headerPanel = new E(cs[1]);
62719         this.headerPanel.enableDisplayMode("block");
62720
62721         this.scroller = new E(cs[2]);
62722         this.scrollSizer = new E(this.scroller.dom.firstChild);
62723
62724         this.lockedWrap = new E(cs[3]);
62725         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62726         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62727
62728         this.mainWrap = new E(cs[4]);
62729         this.mainHd = new E(this.mainWrap.dom.firstChild);
62730         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
62731
62732         this.footerPanel = new E(cs[5]);
62733         this.footerPanel.enableDisplayMode("block");
62734
62735         this.resizeProxy = new E(cs[6]);
62736
62737         this.headerSelector = String.format(
62738            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
62739            this.lockedHd.id, this.mainHd.id
62740         );
62741
62742         this.splitterSelector = String.format(
62743            '#{0} div.x-grid-split, #{1} div.x-grid-split',
62744            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
62745         );
62746     },
62747     idToCssName : function(s)
62748     {
62749         return s.replace(/[^a-z0-9]+/ig, '-');
62750     },
62751
62752     getHeaderCell : function(index){
62753         return Roo.DomQuery.select(this.headerSelector)[index];
62754     },
62755
62756     getHeaderCellMeasure : function(index){
62757         return this.getHeaderCell(index).firstChild;
62758     },
62759
62760     getHeaderCellText : function(index){
62761         return this.getHeaderCell(index).firstChild.firstChild;
62762     },
62763
62764     getLockedTable : function(){
62765         return this.lockedBody.dom.firstChild;
62766     },
62767
62768     getBodyTable : function(){
62769         return this.mainBody.dom.firstChild;
62770     },
62771
62772     getLockedRow : function(index){
62773         return this.getLockedTable().rows[index];
62774     },
62775
62776     getRow : function(index){
62777         return this.getBodyTable().rows[index];
62778     },
62779
62780     getRowComposite : function(index){
62781         if(!this.rowEl){
62782             this.rowEl = new Roo.CompositeElementLite();
62783         }
62784         var els = [], lrow, mrow;
62785         if(lrow = this.getLockedRow(index)){
62786             els.push(lrow);
62787         }
62788         if(mrow = this.getRow(index)){
62789             els.push(mrow);
62790         }
62791         this.rowEl.elements = els;
62792         return this.rowEl;
62793     },
62794     /**
62795      * Gets the 'td' of the cell
62796      * 
62797      * @param {Integer} rowIndex row to select
62798      * @param {Integer} colIndex column to select
62799      * 
62800      * @return {Object} 
62801      */
62802     getCell : function(rowIndex, colIndex){
62803         var locked = this.cm.getLockedCount();
62804         var source;
62805         if(colIndex < locked){
62806             source = this.lockedBody.dom.firstChild;
62807         }else{
62808             source = this.mainBody.dom.firstChild;
62809             colIndex -= locked;
62810         }
62811         return source.rows[rowIndex].childNodes[colIndex];
62812     },
62813
62814     getCellText : function(rowIndex, colIndex){
62815         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
62816     },
62817
62818     getCellBox : function(cell){
62819         var b = this.fly(cell).getBox();
62820         if(Roo.isOpera){ // opera fails to report the Y
62821             b.y = cell.offsetTop + this.mainBody.getY();
62822         }
62823         return b;
62824     },
62825
62826     getCellIndex : function(cell){
62827         var id = String(cell.className).match(this.cellRE);
62828         if(id){
62829             return parseInt(id[1], 10);
62830         }
62831         return 0;
62832     },
62833
62834     findHeaderIndex : function(n){
62835         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62836         return r ? this.getCellIndex(r) : false;
62837     },
62838
62839     findHeaderCell : function(n){
62840         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62841         return r ? r : false;
62842     },
62843
62844     findRowIndex : function(n){
62845         if(!n){
62846             return false;
62847         }
62848         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
62849         return r ? r.rowIndex : false;
62850     },
62851
62852     findCellIndex : function(node){
62853         var stop = this.el.dom;
62854         while(node && node != stop){
62855             if(this.findRE.test(node.className)){
62856                 return this.getCellIndex(node);
62857             }
62858             node = node.parentNode;
62859         }
62860         return false;
62861     },
62862
62863     getColumnId : function(index){
62864         return this.cm.getColumnId(index);
62865     },
62866
62867     getSplitters : function()
62868     {
62869         if(this.splitterSelector){
62870            return Roo.DomQuery.select(this.splitterSelector);
62871         }else{
62872             return null;
62873       }
62874     },
62875
62876     getSplitter : function(index){
62877         return this.getSplitters()[index];
62878     },
62879
62880     onRowOver : function(e, t){
62881         var row;
62882         if((row = this.findRowIndex(t)) !== false){
62883             this.getRowComposite(row).addClass("x-grid-row-over");
62884         }
62885     },
62886
62887     onRowOut : function(e, t){
62888         var row;
62889         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
62890             this.getRowComposite(row).removeClass("x-grid-row-over");
62891         }
62892     },
62893
62894     renderHeaders : function(){
62895         var cm = this.cm;
62896         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
62897         var cb = [], lb = [], sb = [], lsb = [], p = {};
62898         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62899             p.cellId = "x-grid-hd-0-" + i;
62900             p.splitId = "x-grid-csplit-0-" + i;
62901             p.id = cm.getColumnId(i);
62902             p.value = cm.getColumnHeader(i) || "";
62903             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
62904             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
62905             if(!cm.isLocked(i)){
62906                 cb[cb.length] = ct.apply(p);
62907                 sb[sb.length] = st.apply(p);
62908             }else{
62909                 lb[lb.length] = ct.apply(p);
62910                 lsb[lsb.length] = st.apply(p);
62911             }
62912         }
62913         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
62914                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
62915     },
62916
62917     updateHeaders : function(){
62918         var html = this.renderHeaders();
62919         this.lockedHd.update(html[0]);
62920         this.mainHd.update(html[1]);
62921     },
62922
62923     /**
62924      * Focuses the specified row.
62925      * @param {Number} row The row index
62926      */
62927     focusRow : function(row)
62928     {
62929         //Roo.log('GridView.focusRow');
62930         var x = this.scroller.dom.scrollLeft;
62931         this.focusCell(row, 0, false);
62932         this.scroller.dom.scrollLeft = x;
62933     },
62934
62935     /**
62936      * Focuses the specified cell.
62937      * @param {Number} row The row index
62938      * @param {Number} col The column index
62939      * @param {Boolean} hscroll false to disable horizontal scrolling
62940      */
62941     focusCell : function(row, col, hscroll)
62942     {
62943         //Roo.log('GridView.focusCell');
62944         var el = this.ensureVisible(row, col, hscroll);
62945         this.focusEl.alignTo(el, "tl-tl");
62946         if(Roo.isGecko){
62947             this.focusEl.focus();
62948         }else{
62949             this.focusEl.focus.defer(1, this.focusEl);
62950         }
62951     },
62952
62953     /**
62954      * Scrolls the specified cell into view
62955      * @param {Number} row The row index
62956      * @param {Number} col The column index
62957      * @param {Boolean} hscroll false to disable horizontal scrolling
62958      */
62959     ensureVisible : function(row, col, hscroll)
62960     {
62961         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
62962         //return null; //disable for testing.
62963         if(typeof row != "number"){
62964             row = row.rowIndex;
62965         }
62966         if(row < 0 && row >= this.ds.getCount()){
62967             return  null;
62968         }
62969         col = (col !== undefined ? col : 0);
62970         var cm = this.grid.colModel;
62971         while(cm.isHidden(col)){
62972             col++;
62973         }
62974
62975         var el = this.getCell(row, col);
62976         if(!el){
62977             return null;
62978         }
62979         var c = this.scroller.dom;
62980
62981         var ctop = parseInt(el.offsetTop, 10);
62982         var cleft = parseInt(el.offsetLeft, 10);
62983         var cbot = ctop + el.offsetHeight;
62984         var cright = cleft + el.offsetWidth;
62985         
62986         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
62987         var stop = parseInt(c.scrollTop, 10);
62988         var sleft = parseInt(c.scrollLeft, 10);
62989         var sbot = stop + ch;
62990         var sright = sleft + c.clientWidth;
62991         /*
62992         Roo.log('GridView.ensureVisible:' +
62993                 ' ctop:' + ctop +
62994                 ' c.clientHeight:' + c.clientHeight +
62995                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
62996                 ' stop:' + stop +
62997                 ' cbot:' + cbot +
62998                 ' sbot:' + sbot +
62999                 ' ch:' + ch  
63000                 );
63001         */
63002         if(ctop < stop){
63003             c.scrollTop = ctop;
63004             //Roo.log("set scrolltop to ctop DISABLE?");
63005         }else if(cbot > sbot){
63006             //Roo.log("set scrolltop to cbot-ch");
63007             c.scrollTop = cbot-ch;
63008         }
63009         
63010         if(hscroll !== false){
63011             if(cleft < sleft){
63012                 c.scrollLeft = cleft;
63013             }else if(cright > sright){
63014                 c.scrollLeft = cright-c.clientWidth;
63015             }
63016         }
63017          
63018         return el;
63019     },
63020
63021     updateColumns : function(){
63022         this.grid.stopEditing();
63023         var cm = this.grid.colModel, colIds = this.getColumnIds();
63024         //var totalWidth = cm.getTotalWidth();
63025         var pos = 0;
63026         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63027             //if(cm.isHidden(i)) continue;
63028             var w = cm.getColumnWidth(i);
63029             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63030             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63031         }
63032         this.updateSplitters();
63033     },
63034
63035     generateRules : function(cm){
63036         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63037         Roo.util.CSS.removeStyleSheet(rulesId);
63038         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63039             var cid = cm.getColumnId(i);
63040             var align = '';
63041             if(cm.config[i].align){
63042                 align = 'text-align:'+cm.config[i].align+';';
63043             }
63044             var hidden = '';
63045             if(cm.isHidden(i)){
63046                 hidden = 'display:none;';
63047             }
63048             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63049             ruleBuf.push(
63050                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63051                     this.hdSelector, cid, " {\n", align, width, "}\n",
63052                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
63053                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
63054         }
63055         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63056     },
63057
63058     updateSplitters : function(){
63059         var cm = this.cm, s = this.getSplitters();
63060         if(s){ // splitters not created yet
63061             var pos = 0, locked = true;
63062             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63063                 if(cm.isHidden(i)) {
63064                     continue;
63065                 }
63066                 var w = cm.getColumnWidth(i); // make sure it's a number
63067                 if(!cm.isLocked(i) && locked){
63068                     pos = 0;
63069                     locked = false;
63070                 }
63071                 pos += w;
63072                 s[i].style.left = (pos-this.splitOffset) + "px";
63073             }
63074         }
63075     },
63076
63077     handleHiddenChange : function(colModel, colIndex, hidden){
63078         if(hidden){
63079             this.hideColumn(colIndex);
63080         }else{
63081             this.unhideColumn(colIndex);
63082         }
63083     },
63084
63085     hideColumn : function(colIndex){
63086         var cid = this.getColumnId(colIndex);
63087         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63088         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63089         if(Roo.isSafari){
63090             this.updateHeaders();
63091         }
63092         this.updateSplitters();
63093         this.layout();
63094     },
63095
63096     unhideColumn : function(colIndex){
63097         var cid = this.getColumnId(colIndex);
63098         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63099         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63100
63101         if(Roo.isSafari){
63102             this.updateHeaders();
63103         }
63104         this.updateSplitters();
63105         this.layout();
63106     },
63107
63108     insertRows : function(dm, firstRow, lastRow, isUpdate){
63109         if(firstRow == 0 && lastRow == dm.getCount()-1){
63110             this.refresh();
63111         }else{
63112             if(!isUpdate){
63113                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63114             }
63115             var s = this.getScrollState();
63116             var markup = this.renderRows(firstRow, lastRow);
63117             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63118             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63119             this.restoreScroll(s);
63120             if(!isUpdate){
63121                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63122                 this.syncRowHeights(firstRow, lastRow);
63123                 this.stripeRows(firstRow);
63124                 this.layout();
63125             }
63126         }
63127     },
63128
63129     bufferRows : function(markup, target, index){
63130         var before = null, trows = target.rows, tbody = target.tBodies[0];
63131         if(index < trows.length){
63132             before = trows[index];
63133         }
63134         var b = document.createElement("div");
63135         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63136         var rows = b.firstChild.rows;
63137         for(var i = 0, len = rows.length; i < len; i++){
63138             if(before){
63139                 tbody.insertBefore(rows[0], before);
63140             }else{
63141                 tbody.appendChild(rows[0]);
63142             }
63143         }
63144         b.innerHTML = "";
63145         b = null;
63146     },
63147
63148     deleteRows : function(dm, firstRow, lastRow){
63149         if(dm.getRowCount()<1){
63150             this.fireEvent("beforerefresh", this);
63151             this.mainBody.update("");
63152             this.lockedBody.update("");
63153             this.fireEvent("refresh", this);
63154         }else{
63155             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63156             var bt = this.getBodyTable();
63157             var tbody = bt.firstChild;
63158             var rows = bt.rows;
63159             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63160                 tbody.removeChild(rows[firstRow]);
63161             }
63162             this.stripeRows(firstRow);
63163             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63164         }
63165     },
63166
63167     updateRows : function(dataSource, firstRow, lastRow){
63168         var s = this.getScrollState();
63169         this.refresh();
63170         this.restoreScroll(s);
63171     },
63172
63173     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63174         if(!noRefresh){
63175            this.refresh();
63176         }
63177         this.updateHeaderSortState();
63178     },
63179
63180     getScrollState : function(){
63181         
63182         var sb = this.scroller.dom;
63183         return {left: sb.scrollLeft, top: sb.scrollTop};
63184     },
63185
63186     stripeRows : function(startRow){
63187         if(!this.grid.stripeRows || this.ds.getCount() < 1){
63188             return;
63189         }
63190         startRow = startRow || 0;
63191         var rows = this.getBodyTable().rows;
63192         var lrows = this.getLockedTable().rows;
63193         var cls = ' x-grid-row-alt ';
63194         for(var i = startRow, len = rows.length; i < len; i++){
63195             var row = rows[i], lrow = lrows[i];
63196             var isAlt = ((i+1) % 2 == 0);
63197             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63198             if(isAlt == hasAlt){
63199                 continue;
63200             }
63201             if(isAlt){
63202                 row.className += " x-grid-row-alt";
63203             }else{
63204                 row.className = row.className.replace("x-grid-row-alt", "");
63205             }
63206             if(lrow){
63207                 lrow.className = row.className;
63208             }
63209         }
63210     },
63211
63212     restoreScroll : function(state){
63213         //Roo.log('GridView.restoreScroll');
63214         var sb = this.scroller.dom;
63215         sb.scrollLeft = state.left;
63216         sb.scrollTop = state.top;
63217         this.syncScroll();
63218     },
63219
63220     syncScroll : function(){
63221         //Roo.log('GridView.syncScroll');
63222         var sb = this.scroller.dom;
63223         var sh = this.mainHd.dom;
63224         var bs = this.mainBody.dom;
63225         var lv = this.lockedBody.dom;
63226         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63227         lv.scrollTop = bs.scrollTop = sb.scrollTop;
63228     },
63229
63230     handleScroll : function(e){
63231         this.syncScroll();
63232         var sb = this.scroller.dom;
63233         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63234         e.stopEvent();
63235     },
63236
63237     handleWheel : function(e){
63238         var d = e.getWheelDelta();
63239         this.scroller.dom.scrollTop -= d*22;
63240         // set this here to prevent jumpy scrolling on large tables
63241         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63242         e.stopEvent();
63243     },
63244
63245     renderRows : function(startRow, endRow){
63246         // pull in all the crap needed to render rows
63247         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63248         var colCount = cm.getColumnCount();
63249
63250         if(ds.getCount() < 1){
63251             return ["", ""];
63252         }
63253
63254         // build a map for all the columns
63255         var cs = [];
63256         for(var i = 0; i < colCount; i++){
63257             var name = cm.getDataIndex(i);
63258             cs[i] = {
63259                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63260                 renderer : cm.getRenderer(i),
63261                 id : cm.getColumnId(i),
63262                 locked : cm.isLocked(i),
63263                 has_editor : cm.isCellEditable(i)
63264             };
63265         }
63266
63267         startRow = startRow || 0;
63268         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63269
63270         // records to render
63271         var rs = ds.getRange(startRow, endRow);
63272
63273         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63274     },
63275
63276     // As much as I hate to duplicate code, this was branched because FireFox really hates
63277     // [].join("") on strings. The performance difference was substantial enough to
63278     // branch this function
63279     doRender : Roo.isGecko ?
63280             function(cs, rs, ds, startRow, colCount, stripe){
63281                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63282                 // buffers
63283                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63284                 
63285                 var hasListener = this.grid.hasListener('rowclass');
63286                 var rowcfg = {};
63287                 for(var j = 0, len = rs.length; j < len; j++){
63288                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63289                     for(var i = 0; i < colCount; i++){
63290                         c = cs[i];
63291                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63292                         p.id = c.id;
63293                         p.css = p.attr = "";
63294                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63295                         if(p.value == undefined || p.value === "") {
63296                             p.value = "&#160;";
63297                         }
63298                         if(c.has_editor){
63299                             p.css += ' x-grid-editable-cell';
63300                         }
63301                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63302                             p.css +=  ' x-grid-dirty-cell';
63303                         }
63304                         var markup = ct.apply(p);
63305                         if(!c.locked){
63306                             cb+= markup;
63307                         }else{
63308                             lcb+= markup;
63309                         }
63310                     }
63311                     var alt = [];
63312                     if(stripe && ((rowIndex+1) % 2 == 0)){
63313                         alt.push("x-grid-row-alt")
63314                     }
63315                     if(r.dirty){
63316                         alt.push(  " x-grid-dirty-row");
63317                     }
63318                     rp.cells = lcb;
63319                     if(this.getRowClass){
63320                         alt.push(this.getRowClass(r, rowIndex));
63321                     }
63322                     if (hasListener) {
63323                         rowcfg = {
63324                              
63325                             record: r,
63326                             rowIndex : rowIndex,
63327                             rowClass : ''
63328                         };
63329                         this.grid.fireEvent('rowclass', this, rowcfg);
63330                         alt.push(rowcfg.rowClass);
63331                     }
63332                     rp.alt = alt.join(" ");
63333                     lbuf+= rt.apply(rp);
63334                     rp.cells = cb;
63335                     buf+=  rt.apply(rp);
63336                 }
63337                 return [lbuf, buf];
63338             } :
63339             function(cs, rs, ds, startRow, colCount, stripe){
63340                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63341                 // buffers
63342                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63343                 var hasListener = this.grid.hasListener('rowclass');
63344  
63345                 var rowcfg = {};
63346                 for(var j = 0, len = rs.length; j < len; j++){
63347                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63348                     for(var i = 0; i < colCount; i++){
63349                         c = cs[i];
63350                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63351                         p.id = c.id;
63352                         p.css = p.attr = "";
63353                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63354                         if(p.value == undefined || p.value === "") {
63355                             p.value = "&#160;";
63356                         }
63357                         //Roo.log(c);
63358                          if(c.has_editor){
63359                             p.css += ' x-grid-editable-cell';
63360                         }
63361                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63362                             p.css += ' x-grid-dirty-cell' 
63363                         }
63364                         
63365                         var markup = ct.apply(p);
63366                         if(!c.locked){
63367                             cb[cb.length] = markup;
63368                         }else{
63369                             lcb[lcb.length] = markup;
63370                         }
63371                     }
63372                     var alt = [];
63373                     if(stripe && ((rowIndex+1) % 2 == 0)){
63374                         alt.push( "x-grid-row-alt");
63375                     }
63376                     if(r.dirty){
63377                         alt.push(" x-grid-dirty-row");
63378                     }
63379                     rp.cells = lcb;
63380                     if(this.getRowClass){
63381                         alt.push( this.getRowClass(r, rowIndex));
63382                     }
63383                     if (hasListener) {
63384                         rowcfg = {
63385                              
63386                             record: r,
63387                             rowIndex : rowIndex,
63388                             rowClass : ''
63389                         };
63390                         this.grid.fireEvent('rowclass', this, rowcfg);
63391                         alt.push(rowcfg.rowClass);
63392                     }
63393                     
63394                     rp.alt = alt.join(" ");
63395                     rp.cells = lcb.join("");
63396                     lbuf[lbuf.length] = rt.apply(rp);
63397                     rp.cells = cb.join("");
63398                     buf[buf.length] =  rt.apply(rp);
63399                 }
63400                 return [lbuf.join(""), buf.join("")];
63401             },
63402
63403     renderBody : function(){
63404         var markup = this.renderRows();
63405         var bt = this.templates.body;
63406         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63407     },
63408
63409     /**
63410      * Refreshes the grid
63411      * @param {Boolean} headersToo
63412      */
63413     refresh : function(headersToo){
63414         this.fireEvent("beforerefresh", this);
63415         this.grid.stopEditing();
63416         var result = this.renderBody();
63417         this.lockedBody.update(result[0]);
63418         this.mainBody.update(result[1]);
63419         if(headersToo === true){
63420             this.updateHeaders();
63421             this.updateColumns();
63422             this.updateSplitters();
63423             this.updateHeaderSortState();
63424         }
63425         this.syncRowHeights();
63426         this.layout();
63427         this.fireEvent("refresh", this);
63428     },
63429
63430     handleColumnMove : function(cm, oldIndex, newIndex){
63431         this.indexMap = null;
63432         var s = this.getScrollState();
63433         this.refresh(true);
63434         this.restoreScroll(s);
63435         this.afterMove(newIndex);
63436     },
63437
63438     afterMove : function(colIndex){
63439         if(this.enableMoveAnim && Roo.enableFx){
63440             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63441         }
63442         // if multisort - fix sortOrder, and reload..
63443         if (this.grid.dataSource.multiSort) {
63444             // the we can call sort again..
63445             var dm = this.grid.dataSource;
63446             var cm = this.grid.colModel;
63447             var so = [];
63448             for(var i = 0; i < cm.config.length; i++ ) {
63449                 
63450                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63451                     continue; // dont' bother, it's not in sort list or being set.
63452                 }
63453                 
63454                 so.push(cm.config[i].dataIndex);
63455             };
63456             dm.sortOrder = so;
63457             dm.load(dm.lastOptions);
63458             
63459             
63460         }
63461         
63462     },
63463
63464     updateCell : function(dm, rowIndex, dataIndex){
63465         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63466         if(typeof colIndex == "undefined"){ // not present in grid
63467             return;
63468         }
63469         var cm = this.grid.colModel;
63470         var cell = this.getCell(rowIndex, colIndex);
63471         var cellText = this.getCellText(rowIndex, colIndex);
63472
63473         var p = {
63474             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63475             id : cm.getColumnId(colIndex),
63476             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63477         };
63478         var renderer = cm.getRenderer(colIndex);
63479         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63480         if(typeof val == "undefined" || val === "") {
63481             val = "&#160;";
63482         }
63483         cellText.innerHTML = val;
63484         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63485         this.syncRowHeights(rowIndex, rowIndex);
63486     },
63487
63488     calcColumnWidth : function(colIndex, maxRowsToMeasure){
63489         var maxWidth = 0;
63490         if(this.grid.autoSizeHeaders){
63491             var h = this.getHeaderCellMeasure(colIndex);
63492             maxWidth = Math.max(maxWidth, h.scrollWidth);
63493         }
63494         var tb, index;
63495         if(this.cm.isLocked(colIndex)){
63496             tb = this.getLockedTable();
63497             index = colIndex;
63498         }else{
63499             tb = this.getBodyTable();
63500             index = colIndex - this.cm.getLockedCount();
63501         }
63502         if(tb && tb.rows){
63503             var rows = tb.rows;
63504             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63505             for(var i = 0; i < stopIndex; i++){
63506                 var cell = rows[i].childNodes[index].firstChild;
63507                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63508             }
63509         }
63510         return maxWidth + /*margin for error in IE*/ 5;
63511     },
63512     /**
63513      * Autofit a column to its content.
63514      * @param {Number} colIndex
63515      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63516      */
63517      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63518          if(this.cm.isHidden(colIndex)){
63519              return; // can't calc a hidden column
63520          }
63521         if(forceMinSize){
63522             var cid = this.cm.getColumnId(colIndex);
63523             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63524            if(this.grid.autoSizeHeaders){
63525                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63526            }
63527         }
63528         var newWidth = this.calcColumnWidth(colIndex);
63529         this.cm.setColumnWidth(colIndex,
63530             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63531         if(!suppressEvent){
63532             this.grid.fireEvent("columnresize", colIndex, newWidth);
63533         }
63534     },
63535
63536     /**
63537      * Autofits all columns to their content and then expands to fit any extra space in the grid
63538      */
63539      autoSizeColumns : function(){
63540         var cm = this.grid.colModel;
63541         var colCount = cm.getColumnCount();
63542         for(var i = 0; i < colCount; i++){
63543             this.autoSizeColumn(i, true, true);
63544         }
63545         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63546             this.fitColumns();
63547         }else{
63548             this.updateColumns();
63549             this.layout();
63550         }
63551     },
63552
63553     /**
63554      * Autofits all columns to the grid's width proportionate with their current size
63555      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63556      */
63557     fitColumns : function(reserveScrollSpace){
63558         var cm = this.grid.colModel;
63559         var colCount = cm.getColumnCount();
63560         var cols = [];
63561         var width = 0;
63562         var i, w;
63563         for (i = 0; i < colCount; i++){
63564             if(!cm.isHidden(i) && !cm.isFixed(i)){
63565                 w = cm.getColumnWidth(i);
63566                 cols.push(i);
63567                 cols.push(w);
63568                 width += w;
63569             }
63570         }
63571         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63572         if(reserveScrollSpace){
63573             avail -= 17;
63574         }
63575         var frac = (avail - cm.getTotalWidth())/width;
63576         while (cols.length){
63577             w = cols.pop();
63578             i = cols.pop();
63579             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63580         }
63581         this.updateColumns();
63582         this.layout();
63583     },
63584
63585     onRowSelect : function(rowIndex){
63586         var row = this.getRowComposite(rowIndex);
63587         row.addClass("x-grid-row-selected");
63588     },
63589
63590     onRowDeselect : function(rowIndex){
63591         var row = this.getRowComposite(rowIndex);
63592         row.removeClass("x-grid-row-selected");
63593     },
63594
63595     onCellSelect : function(row, col){
63596         var cell = this.getCell(row, col);
63597         if(cell){
63598             Roo.fly(cell).addClass("x-grid-cell-selected");
63599         }
63600     },
63601
63602     onCellDeselect : function(row, col){
63603         var cell = this.getCell(row, col);
63604         if(cell){
63605             Roo.fly(cell).removeClass("x-grid-cell-selected");
63606         }
63607     },
63608
63609     updateHeaderSortState : function(){
63610         
63611         // sort state can be single { field: xxx, direction : yyy}
63612         // or   { xxx=>ASC , yyy : DESC ..... }
63613         
63614         var mstate = {};
63615         if (!this.ds.multiSort) { 
63616             var state = this.ds.getSortState();
63617             if(!state){
63618                 return;
63619             }
63620             mstate[state.field] = state.direction;
63621             // FIXME... - this is not used here.. but might be elsewhere..
63622             this.sortState = state;
63623             
63624         } else {
63625             mstate = this.ds.sortToggle;
63626         }
63627         //remove existing sort classes..
63628         
63629         var sc = this.sortClasses;
63630         var hds = this.el.select(this.headerSelector).removeClass(sc);
63631         
63632         for(var f in mstate) {
63633         
63634             var sortColumn = this.cm.findColumnIndex(f);
63635             
63636             if(sortColumn != -1){
63637                 var sortDir = mstate[f];        
63638                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63639             }
63640         }
63641         
63642          
63643         
63644     },
63645
63646
63647     handleHeaderClick : function(g, index,e){
63648         
63649         Roo.log("header click");
63650         
63651         if (Roo.isTouch) {
63652             // touch events on header are handled by context
63653             this.handleHdCtx(g,index,e);
63654             return;
63655         }
63656         
63657         
63658         if(this.headersDisabled){
63659             return;
63660         }
63661         var dm = g.dataSource, cm = g.colModel;
63662         if(!cm.isSortable(index)){
63663             return;
63664         }
63665         g.stopEditing();
63666         
63667         if (dm.multiSort) {
63668             // update the sortOrder
63669             var so = [];
63670             for(var i = 0; i < cm.config.length; i++ ) {
63671                 
63672                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63673                     continue; // dont' bother, it's not in sort list or being set.
63674                 }
63675                 
63676                 so.push(cm.config[i].dataIndex);
63677             };
63678             dm.sortOrder = so;
63679         }
63680         
63681         
63682         dm.sort(cm.getDataIndex(index));
63683     },
63684
63685
63686     destroy : function(){
63687         if(this.colMenu){
63688             this.colMenu.removeAll();
63689             Roo.menu.MenuMgr.unregister(this.colMenu);
63690             this.colMenu.getEl().remove();
63691             delete this.colMenu;
63692         }
63693         if(this.hmenu){
63694             this.hmenu.removeAll();
63695             Roo.menu.MenuMgr.unregister(this.hmenu);
63696             this.hmenu.getEl().remove();
63697             delete this.hmenu;
63698         }
63699         if(this.grid.enableColumnMove){
63700             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63701             if(dds){
63702                 for(var dd in dds){
63703                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
63704                         var elid = dds[dd].dragElId;
63705                         dds[dd].unreg();
63706                         Roo.get(elid).remove();
63707                     } else if(dds[dd].config.isTarget){
63708                         dds[dd].proxyTop.remove();
63709                         dds[dd].proxyBottom.remove();
63710                         dds[dd].unreg();
63711                     }
63712                     if(Roo.dd.DDM.locationCache[dd]){
63713                         delete Roo.dd.DDM.locationCache[dd];
63714                     }
63715                 }
63716                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63717             }
63718         }
63719         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63720         this.bind(null, null);
63721         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63722     },
63723
63724     handleLockChange : function(){
63725         this.refresh(true);
63726     },
63727
63728     onDenyColumnLock : function(){
63729
63730     },
63731
63732     onDenyColumnHide : function(){
63733
63734     },
63735
63736     handleHdMenuClick : function(item){
63737         var index = this.hdCtxIndex;
63738         var cm = this.cm, ds = this.ds;
63739         switch(item.id){
63740             case "asc":
63741                 ds.sort(cm.getDataIndex(index), "ASC");
63742                 break;
63743             case "desc":
63744                 ds.sort(cm.getDataIndex(index), "DESC");
63745                 break;
63746             case "lock":
63747                 var lc = cm.getLockedCount();
63748                 if(cm.getColumnCount(true) <= lc+1){
63749                     this.onDenyColumnLock();
63750                     return;
63751                 }
63752                 if(lc != index){
63753                     cm.setLocked(index, true, true);
63754                     cm.moveColumn(index, lc);
63755                     this.grid.fireEvent("columnmove", index, lc);
63756                 }else{
63757                     cm.setLocked(index, true);
63758                 }
63759             break;
63760             case "unlock":
63761                 var lc = cm.getLockedCount();
63762                 if((lc-1) != index){
63763                     cm.setLocked(index, false, true);
63764                     cm.moveColumn(index, lc-1);
63765                     this.grid.fireEvent("columnmove", index, lc-1);
63766                 }else{
63767                     cm.setLocked(index, false);
63768                 }
63769             break;
63770             case 'wider': // used to expand cols on touch..
63771             case 'narrow':
63772                 var cw = cm.getColumnWidth(index);
63773                 cw += (item.id == 'wider' ? 1 : -1) * 50;
63774                 cw = Math.max(0, cw);
63775                 cw = Math.min(cw,4000);
63776                 cm.setColumnWidth(index, cw);
63777                 break;
63778                 
63779             default:
63780                 index = cm.getIndexById(item.id.substr(4));
63781                 if(index != -1){
63782                     if(item.checked && cm.getColumnCount(true) <= 1){
63783                         this.onDenyColumnHide();
63784                         return false;
63785                     }
63786                     cm.setHidden(index, item.checked);
63787                 }
63788         }
63789         return true;
63790     },
63791
63792     beforeColMenuShow : function(){
63793         var cm = this.cm,  colCount = cm.getColumnCount();
63794         this.colMenu.removeAll();
63795         
63796         var items = [];
63797         for(var i = 0; i < colCount; i++){
63798             items.push({
63799                 id: "col-"+cm.getColumnId(i),
63800                 text: cm.getColumnHeader(i),
63801                 checked: !cm.isHidden(i),
63802                 hideOnClick:false
63803             });
63804         }
63805         
63806         if (this.grid.sortColMenu) {
63807             items.sort(function(a,b) {
63808                 if (a.text == b.text) {
63809                     return 0;
63810                 }
63811                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
63812             });
63813         }
63814         
63815         for(var i = 0; i < colCount; i++){
63816             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
63817         }
63818     },
63819
63820     handleHdCtx : function(g, index, e){
63821         e.stopEvent();
63822         var hd = this.getHeaderCell(index);
63823         this.hdCtxIndex = index;
63824         var ms = this.hmenu.items, cm = this.cm;
63825         ms.get("asc").setDisabled(!cm.isSortable(index));
63826         ms.get("desc").setDisabled(!cm.isSortable(index));
63827         if(this.grid.enableColLock !== false){
63828             ms.get("lock").setDisabled(cm.isLocked(index));
63829             ms.get("unlock").setDisabled(!cm.isLocked(index));
63830         }
63831         this.hmenu.show(hd, "tl-bl");
63832     },
63833
63834     handleHdOver : function(e){
63835         var hd = this.findHeaderCell(e.getTarget());
63836         if(hd && !this.headersDisabled){
63837             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
63838                this.fly(hd).addClass("x-grid-hd-over");
63839             }
63840         }
63841     },
63842
63843     handleHdOut : function(e){
63844         var hd = this.findHeaderCell(e.getTarget());
63845         if(hd){
63846             this.fly(hd).removeClass("x-grid-hd-over");
63847         }
63848     },
63849
63850     handleSplitDblClick : function(e, t){
63851         var i = this.getCellIndex(t);
63852         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
63853             this.autoSizeColumn(i, true);
63854             this.layout();
63855         }
63856     },
63857
63858     render : function(){
63859
63860         var cm = this.cm;
63861         var colCount = cm.getColumnCount();
63862
63863         if(this.grid.monitorWindowResize === true){
63864             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
63865         }
63866         var header = this.renderHeaders();
63867         var body = this.templates.body.apply({rows:""});
63868         var html = this.templates.master.apply({
63869             lockedBody: body,
63870             body: body,
63871             lockedHeader: header[0],
63872             header: header[1]
63873         });
63874
63875         //this.updateColumns();
63876
63877         this.grid.getGridEl().dom.innerHTML = html;
63878
63879         this.initElements();
63880         
63881         // a kludge to fix the random scolling effect in webkit
63882         this.el.on("scroll", function() {
63883             this.el.dom.scrollTop=0; // hopefully not recursive..
63884         },this);
63885
63886         this.scroller.on("scroll", this.handleScroll, this);
63887         this.lockedBody.on("mousewheel", this.handleWheel, this);
63888         this.mainBody.on("mousewheel", this.handleWheel, this);
63889
63890         this.mainHd.on("mouseover", this.handleHdOver, this);
63891         this.mainHd.on("mouseout", this.handleHdOut, this);
63892         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
63893                 {delegate: "."+this.splitClass});
63894
63895         this.lockedHd.on("mouseover", this.handleHdOver, this);
63896         this.lockedHd.on("mouseout", this.handleHdOut, this);
63897         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
63898                 {delegate: "."+this.splitClass});
63899
63900         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
63901             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63902         }
63903
63904         this.updateSplitters();
63905
63906         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
63907             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63908             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63909         }
63910
63911         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
63912             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
63913             this.hmenu.add(
63914                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
63915                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
63916             );
63917             if(this.grid.enableColLock !== false){
63918                 this.hmenu.add('-',
63919                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
63920                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
63921                 );
63922             }
63923             if (Roo.isTouch) {
63924                  this.hmenu.add('-',
63925                     {id:"wider", text: this.columnsWiderText},
63926                     {id:"narrow", text: this.columnsNarrowText }
63927                 );
63928                 
63929                  
63930             }
63931             
63932             if(this.grid.enableColumnHide !== false){
63933
63934                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
63935                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
63936                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
63937
63938                 this.hmenu.add('-',
63939                     {id:"columns", text: this.columnsText, menu: this.colMenu}
63940                 );
63941             }
63942             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
63943
63944             this.grid.on("headercontextmenu", this.handleHdCtx, this);
63945         }
63946
63947         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
63948             this.dd = new Roo.grid.GridDragZone(this.grid, {
63949                 ddGroup : this.grid.ddGroup || 'GridDD'
63950             });
63951             
63952         }
63953
63954         /*
63955         for(var i = 0; i < colCount; i++){
63956             if(cm.isHidden(i)){
63957                 this.hideColumn(i);
63958             }
63959             if(cm.config[i].align){
63960                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
63961                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
63962             }
63963         }*/
63964         
63965         this.updateHeaderSortState();
63966
63967         this.beforeInitialResize();
63968         this.layout(true);
63969
63970         // two part rendering gives faster view to the user
63971         this.renderPhase2.defer(1, this);
63972     },
63973
63974     renderPhase2 : function(){
63975         // render the rows now
63976         this.refresh();
63977         if(this.grid.autoSizeColumns){
63978             this.autoSizeColumns();
63979         }
63980     },
63981
63982     beforeInitialResize : function(){
63983
63984     },
63985
63986     onColumnSplitterMoved : function(i, w){
63987         this.userResized = true;
63988         var cm = this.grid.colModel;
63989         cm.setColumnWidth(i, w, true);
63990         var cid = cm.getColumnId(i);
63991         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63992         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63993         this.updateSplitters();
63994         this.layout();
63995         this.grid.fireEvent("columnresize", i, w);
63996     },
63997
63998     syncRowHeights : function(startIndex, endIndex){
63999         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
64000             startIndex = startIndex || 0;
64001             var mrows = this.getBodyTable().rows;
64002             var lrows = this.getLockedTable().rows;
64003             var len = mrows.length-1;
64004             endIndex = Math.min(endIndex || len, len);
64005             for(var i = startIndex; i <= endIndex; i++){
64006                 var m = mrows[i], l = lrows[i];
64007                 var h = Math.max(m.offsetHeight, l.offsetHeight);
64008                 m.style.height = l.style.height = h + "px";
64009             }
64010         }
64011     },
64012
64013     layout : function(initialRender, is2ndPass)
64014     {
64015         var g = this.grid;
64016         var auto = g.autoHeight;
64017         var scrollOffset = 16;
64018         var c = g.getGridEl(), cm = this.cm,
64019                 expandCol = g.autoExpandColumn,
64020                 gv = this;
64021         //c.beginMeasure();
64022
64023         if(!c.dom.offsetWidth){ // display:none?
64024             if(initialRender){
64025                 this.lockedWrap.show();
64026                 this.mainWrap.show();
64027             }
64028             return;
64029         }
64030
64031         var hasLock = this.cm.isLocked(0);
64032
64033         var tbh = this.headerPanel.getHeight();
64034         var bbh = this.footerPanel.getHeight();
64035
64036         if(auto){
64037             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64038             var newHeight = ch + c.getBorderWidth("tb");
64039             if(g.maxHeight){
64040                 newHeight = Math.min(g.maxHeight, newHeight);
64041             }
64042             c.setHeight(newHeight);
64043         }
64044
64045         if(g.autoWidth){
64046             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64047         }
64048
64049         var s = this.scroller;
64050
64051         var csize = c.getSize(true);
64052
64053         this.el.setSize(csize.width, csize.height);
64054
64055         this.headerPanel.setWidth(csize.width);
64056         this.footerPanel.setWidth(csize.width);
64057
64058         var hdHeight = this.mainHd.getHeight();
64059         var vw = csize.width;
64060         var vh = csize.height - (tbh + bbh);
64061
64062         s.setSize(vw, vh);
64063
64064         var bt = this.getBodyTable();
64065         
64066         if(cm.getLockedCount() == cm.config.length){
64067             bt = this.getLockedTable();
64068         }
64069         
64070         var ltWidth = hasLock ?
64071                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64072
64073         var scrollHeight = bt.offsetHeight;
64074         var scrollWidth = ltWidth + bt.offsetWidth;
64075         var vscroll = false, hscroll = false;
64076
64077         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64078
64079         var lw = this.lockedWrap, mw = this.mainWrap;
64080         var lb = this.lockedBody, mb = this.mainBody;
64081
64082         setTimeout(function(){
64083             var t = s.dom.offsetTop;
64084             var w = s.dom.clientWidth,
64085                 h = s.dom.clientHeight;
64086
64087             lw.setTop(t);
64088             lw.setSize(ltWidth, h);
64089
64090             mw.setLeftTop(ltWidth, t);
64091             mw.setSize(w-ltWidth, h);
64092
64093             lb.setHeight(h-hdHeight);
64094             mb.setHeight(h-hdHeight);
64095
64096             if(is2ndPass !== true && !gv.userResized && expandCol){
64097                 // high speed resize without full column calculation
64098                 
64099                 var ci = cm.getIndexById(expandCol);
64100                 if (ci < 0) {
64101                     ci = cm.findColumnIndex(expandCol);
64102                 }
64103                 ci = Math.max(0, ci); // make sure it's got at least the first col.
64104                 var expandId = cm.getColumnId(ci);
64105                 var  tw = cm.getTotalWidth(false);
64106                 var currentWidth = cm.getColumnWidth(ci);
64107                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64108                 if(currentWidth != cw){
64109                     cm.setColumnWidth(ci, cw, true);
64110                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64111                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64112                     gv.updateSplitters();
64113                     gv.layout(false, true);
64114                 }
64115             }
64116
64117             if(initialRender){
64118                 lw.show();
64119                 mw.show();
64120             }
64121             //c.endMeasure();
64122         }, 10);
64123     },
64124
64125     onWindowResize : function(){
64126         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64127             return;
64128         }
64129         this.layout();
64130     },
64131
64132     appendFooter : function(parentEl){
64133         return null;
64134     },
64135
64136     sortAscText : "Sort Ascending",
64137     sortDescText : "Sort Descending",
64138     lockText : "Lock Column",
64139     unlockText : "Unlock Column",
64140     columnsText : "Columns",
64141  
64142     columnsWiderText : "Wider",
64143     columnsNarrowText : "Thinner"
64144 });
64145
64146
64147 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64148     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64149     this.proxy.el.addClass('x-grid3-col-dd');
64150 };
64151
64152 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64153     handleMouseDown : function(e){
64154
64155     },
64156
64157     callHandleMouseDown : function(e){
64158         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64159     }
64160 });
64161 /*
64162  * Based on:
64163  * Ext JS Library 1.1.1
64164  * Copyright(c) 2006-2007, Ext JS, LLC.
64165  *
64166  * Originally Released Under LGPL - original licence link has changed is not relivant.
64167  *
64168  * Fork - LGPL
64169  * <script type="text/javascript">
64170  */
64171  /**
64172  * @extends Roo.dd.DDProxy
64173  * @class Roo.grid.SplitDragZone
64174  * Support for Column Header resizing
64175  * @constructor
64176  * @param {Object} config
64177  */
64178 // private
64179 // This is a support class used internally by the Grid components
64180 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64181     this.grid = grid;
64182     this.view = grid.getView();
64183     this.proxy = this.view.resizeProxy;
64184     Roo.grid.SplitDragZone.superclass.constructor.call(
64185         this,
64186         hd, // ID
64187         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64188         {  // CONFIG
64189             dragElId : Roo.id(this.proxy.dom),
64190             resizeFrame:false
64191         }
64192     );
64193     
64194     this.setHandleElId(Roo.id(hd));
64195     if (hd2 !== false) {
64196         this.setOuterHandleElId(Roo.id(hd2));
64197     }
64198     
64199     this.scroll = false;
64200 };
64201 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64202     fly: Roo.Element.fly,
64203
64204     b4StartDrag : function(x, y){
64205         this.view.headersDisabled = true;
64206         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64207                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64208         );
64209         this.proxy.setHeight(h);
64210         
64211         // for old system colWidth really stored the actual width?
64212         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64213         // which in reality did not work.. - it worked only for fixed sizes
64214         // for resizable we need to use actual sizes.
64215         var w = this.cm.getColumnWidth(this.cellIndex);
64216         if (!this.view.mainWrap) {
64217             // bootstrap.
64218             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64219         }
64220         
64221         
64222         
64223         // this was w-this.grid.minColumnWidth;
64224         // doesnt really make sense? - w = thie curren width or the rendered one?
64225         var minw = Math.max(w-this.grid.minColumnWidth, 0);
64226         this.resetConstraints();
64227         this.setXConstraint(minw, 1000);
64228         this.setYConstraint(0, 0);
64229         this.minX = x - minw;
64230         this.maxX = x + 1000;
64231         this.startPos = x;
64232         if (!this.view.mainWrap) { // this is Bootstrap code..
64233             this.getDragEl().style.display='block';
64234         }
64235         
64236         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64237     },
64238
64239
64240     handleMouseDown : function(e){
64241         ev = Roo.EventObject.setEvent(e);
64242         var t = this.fly(ev.getTarget());
64243         if(t.hasClass("x-grid-split")){
64244             this.cellIndex = this.view.getCellIndex(t.dom);
64245             this.split = t.dom;
64246             this.cm = this.grid.colModel;
64247             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64248                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64249             }
64250         }
64251     },
64252
64253     endDrag : function(e){
64254         this.view.headersDisabled = false;
64255         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64256         var diff = endX - this.startPos;
64257         // 
64258         var w = this.cm.getColumnWidth(this.cellIndex);
64259         if (!this.view.mainWrap) {
64260             w = 0;
64261         }
64262         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64263     },
64264
64265     autoOffset : function(){
64266         this.setDelta(0,0);
64267     }
64268 });/*
64269  * Based on:
64270  * Ext JS Library 1.1.1
64271  * Copyright(c) 2006-2007, Ext JS, LLC.
64272  *
64273  * Originally Released Under LGPL - original licence link has changed is not relivant.
64274  *
64275  * Fork - LGPL
64276  * <script type="text/javascript">
64277  */
64278  
64279 // private
64280 // This is a support class used internally by the Grid components
64281 Roo.grid.GridDragZone = function(grid, config){
64282     this.view = grid.getView();
64283     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64284     if(this.view.lockedBody){
64285         this.setHandleElId(Roo.id(this.view.mainBody.dom));
64286         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64287     }
64288     this.scroll = false;
64289     this.grid = grid;
64290     this.ddel = document.createElement('div');
64291     this.ddel.className = 'x-grid-dd-wrap';
64292 };
64293
64294 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64295     ddGroup : "GridDD",
64296
64297     getDragData : function(e){
64298         var t = Roo.lib.Event.getTarget(e);
64299         var rowIndex = this.view.findRowIndex(t);
64300         var sm = this.grid.selModel;
64301             
64302         //Roo.log(rowIndex);
64303         
64304         if (sm.getSelectedCell) {
64305             // cell selection..
64306             if (!sm.getSelectedCell()) {
64307                 return false;
64308             }
64309             if (rowIndex != sm.getSelectedCell()[0]) {
64310                 return false;
64311             }
64312         
64313         }
64314         if (sm.getSelections && sm.getSelections().length < 1) {
64315             return false;
64316         }
64317         
64318         
64319         // before it used to all dragging of unseleted... - now we dont do that.
64320         if(rowIndex !== false){
64321             
64322             // if editorgrid.. 
64323             
64324             
64325             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64326                
64327             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64328               //  
64329             //}
64330             if (e.hasModifier()){
64331                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64332             }
64333             
64334             Roo.log("getDragData");
64335             
64336             return {
64337                 grid: this.grid,
64338                 ddel: this.ddel,
64339                 rowIndex: rowIndex,
64340                 selections: sm.getSelections ? sm.getSelections() : (
64341                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64342             };
64343         }
64344         return false;
64345     },
64346     
64347     
64348     onInitDrag : function(e){
64349         var data = this.dragData;
64350         this.ddel.innerHTML = this.grid.getDragDropText();
64351         this.proxy.update(this.ddel);
64352         // fire start drag?
64353     },
64354
64355     afterRepair : function(){
64356         this.dragging = false;
64357     },
64358
64359     getRepairXY : function(e, data){
64360         return false;
64361     },
64362
64363     onEndDrag : function(data, e){
64364         // fire end drag?
64365     },
64366
64367     onValidDrop : function(dd, e, id){
64368         // fire drag drop?
64369         this.hideProxy();
64370     },
64371
64372     beforeInvalidDrop : function(e, id){
64373
64374     }
64375 });/*
64376  * Based on:
64377  * Ext JS Library 1.1.1
64378  * Copyright(c) 2006-2007, Ext JS, LLC.
64379  *
64380  * Originally Released Under LGPL - original licence link has changed is not relivant.
64381  *
64382  * Fork - LGPL
64383  * <script type="text/javascript">
64384  */
64385  
64386
64387 /**
64388  * @class Roo.grid.ColumnModel
64389  * @extends Roo.util.Observable
64390  * This is the default implementation of a ColumnModel used by the Grid. It defines
64391  * the columns in the grid.
64392  * <br>Usage:<br>
64393  <pre><code>
64394  var colModel = new Roo.grid.ColumnModel([
64395         {header: "Ticker", width: 60, sortable: true, locked: true},
64396         {header: "Company Name", width: 150, sortable: true},
64397         {header: "Market Cap.", width: 100, sortable: true},
64398         {header: "$ Sales", width: 100, sortable: true, renderer: money},
64399         {header: "Employees", width: 100, sortable: true, resizable: false}
64400  ]);
64401  </code></pre>
64402  * <p>
64403  
64404  * The config options listed for this class are options which may appear in each
64405  * individual column definition.
64406  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64407  * @constructor
64408  * @param {Object} config An Array of column config objects. See this class's
64409  * config objects for details.
64410 */
64411 Roo.grid.ColumnModel = function(config){
64412         /**
64413      * The config passed into the constructor
64414      */
64415     this.config = []; //config;
64416     this.lookup = {};
64417
64418     // if no id, create one
64419     // if the column does not have a dataIndex mapping,
64420     // map it to the order it is in the config
64421     for(var i = 0, len = config.length; i < len; i++){
64422         this.addColumn(config[i]);
64423         
64424     }
64425
64426     /**
64427      * The width of columns which have no width specified (defaults to 100)
64428      * @type Number
64429      */
64430     this.defaultWidth = 100;
64431
64432     /**
64433      * Default sortable of columns which have no sortable specified (defaults to false)
64434      * @type Boolean
64435      */
64436     this.defaultSortable = false;
64437
64438     this.addEvents({
64439         /**
64440              * @event widthchange
64441              * Fires when the width of a column changes.
64442              * @param {ColumnModel} this
64443              * @param {Number} columnIndex The column index
64444              * @param {Number} newWidth The new width
64445              */
64446             "widthchange": true,
64447         /**
64448              * @event headerchange
64449              * Fires when the text of a header changes.
64450              * @param {ColumnModel} this
64451              * @param {Number} columnIndex The column index
64452              * @param {Number} newText The new header text
64453              */
64454             "headerchange": true,
64455         /**
64456              * @event hiddenchange
64457              * Fires when a column is hidden or "unhidden".
64458              * @param {ColumnModel} this
64459              * @param {Number} columnIndex The column index
64460              * @param {Boolean} hidden true if hidden, false otherwise
64461              */
64462             "hiddenchange": true,
64463             /**
64464          * @event columnmoved
64465          * Fires when a column is moved.
64466          * @param {ColumnModel} this
64467          * @param {Number} oldIndex
64468          * @param {Number} newIndex
64469          */
64470         "columnmoved" : true,
64471         /**
64472          * @event columlockchange
64473          * Fires when a column's locked state is changed
64474          * @param {ColumnModel} this
64475          * @param {Number} colIndex
64476          * @param {Boolean} locked true if locked
64477          */
64478         "columnlockchange" : true
64479     });
64480     Roo.grid.ColumnModel.superclass.constructor.call(this);
64481 };
64482 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64483     /**
64484      * @cfg {String} header [required] The header text to display in the Grid view.
64485      */
64486         /**
64487      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64488      */
64489         /**
64490      * @cfg {String} smHeader Header at Bootsrap Small width
64491      */
64492         /**
64493      * @cfg {String} mdHeader Header at Bootsrap Medium width
64494      */
64495         /**
64496      * @cfg {String} lgHeader Header at Bootsrap Large width
64497      */
64498         /**
64499      * @cfg {String} xlHeader Header at Bootsrap extra Large width
64500      */
64501     /**
64502      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
64503      * {@link Roo.data.Record} definition from which to draw the column's value. If not
64504      * specified, the column's index is used as an index into the Record's data Array.
64505      */
64506     /**
64507      * @cfg {Number} width  The initial width in pixels of the column. Using this
64508      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64509      */
64510     /**
64511      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
64512      * Defaults to the value of the {@link #defaultSortable} property.
64513      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64514      */
64515     /**
64516      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
64517      */
64518     /**
64519      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
64520      */
64521     /**
64522      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
64523      */
64524     /**
64525      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
64526      */
64527     /**
64528      * @cfg {Function} renderer A function used to generate HTML markup for a cell
64529      * given the cell's data value. See {@link #setRenderer}. If not specified, the
64530      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64531      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64532      */
64533        /**
64534      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
64535      */
64536     /**
64537      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
64538      */
64539     /**
64540      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
64541      */
64542     /**
64543      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
64544      */
64545     /**
64546      * @cfg {String} tooltip mouse over tooltip text
64547      */
64548     /**
64549      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
64550      */
64551     /**
64552      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
64553      */
64554     /**
64555      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
64556      */
64557     /**
64558      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
64559      */
64560         /**
64561      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
64562      */
64563     /**
64564      * Returns the id of the column at the specified index.
64565      * @param {Number} index The column index
64566      * @return {String} the id
64567      */
64568     getColumnId : function(index){
64569         return this.config[index].id;
64570     },
64571
64572     /**
64573      * Returns the column for a specified id.
64574      * @param {String} id The column id
64575      * @return {Object} the column
64576      */
64577     getColumnById : function(id){
64578         return this.lookup[id];
64579     },
64580
64581     
64582     /**
64583      * Returns the column Object for a specified dataIndex.
64584      * @param {String} dataIndex The column dataIndex
64585      * @return {Object|Boolean} the column or false if not found
64586      */
64587     getColumnByDataIndex: function(dataIndex){
64588         var index = this.findColumnIndex(dataIndex);
64589         return index > -1 ? this.config[index] : false;
64590     },
64591     
64592     /**
64593      * Returns the index for a specified column id.
64594      * @param {String} id The column id
64595      * @return {Number} the index, or -1 if not found
64596      */
64597     getIndexById : function(id){
64598         for(var i = 0, len = this.config.length; i < len; i++){
64599             if(this.config[i].id == id){
64600                 return i;
64601             }
64602         }
64603         return -1;
64604     },
64605     
64606     /**
64607      * Returns the index for a specified column dataIndex.
64608      * @param {String} dataIndex The column dataIndex
64609      * @return {Number} the index, or -1 if not found
64610      */
64611     
64612     findColumnIndex : function(dataIndex){
64613         for(var i = 0, len = this.config.length; i < len; i++){
64614             if(this.config[i].dataIndex == dataIndex){
64615                 return i;
64616             }
64617         }
64618         return -1;
64619     },
64620     
64621     
64622     moveColumn : function(oldIndex, newIndex){
64623         var c = this.config[oldIndex];
64624         this.config.splice(oldIndex, 1);
64625         this.config.splice(newIndex, 0, c);
64626         this.dataMap = null;
64627         this.fireEvent("columnmoved", this, oldIndex, newIndex);
64628     },
64629
64630     isLocked : function(colIndex){
64631         return this.config[colIndex].locked === true;
64632     },
64633
64634     setLocked : function(colIndex, value, suppressEvent){
64635         if(this.isLocked(colIndex) == value){
64636             return;
64637         }
64638         this.config[colIndex].locked = value;
64639         if(!suppressEvent){
64640             this.fireEvent("columnlockchange", this, colIndex, value);
64641         }
64642     },
64643
64644     getTotalLockedWidth : function(){
64645         var totalWidth = 0;
64646         for(var i = 0; i < this.config.length; i++){
64647             if(this.isLocked(i) && !this.isHidden(i)){
64648                 this.totalWidth += this.getColumnWidth(i);
64649             }
64650         }
64651         return totalWidth;
64652     },
64653
64654     getLockedCount : function(){
64655         for(var i = 0, len = this.config.length; i < len; i++){
64656             if(!this.isLocked(i)){
64657                 return i;
64658             }
64659         }
64660         
64661         return this.config.length;
64662     },
64663
64664     /**
64665      * Returns the number of columns.
64666      * @return {Number}
64667      */
64668     getColumnCount : function(visibleOnly){
64669         if(visibleOnly === true){
64670             var c = 0;
64671             for(var i = 0, len = this.config.length; i < len; i++){
64672                 if(!this.isHidden(i)){
64673                     c++;
64674                 }
64675             }
64676             return c;
64677         }
64678         return this.config.length;
64679     },
64680
64681     /**
64682      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64683      * @param {Function} fn
64684      * @param {Object} scope (optional)
64685      * @return {Array} result
64686      */
64687     getColumnsBy : function(fn, scope){
64688         var r = [];
64689         for(var i = 0, len = this.config.length; i < len; i++){
64690             var c = this.config[i];
64691             if(fn.call(scope||this, c, i) === true){
64692                 r[r.length] = c;
64693             }
64694         }
64695         return r;
64696     },
64697
64698     /**
64699      * Returns true if the specified column is sortable.
64700      * @param {Number} col The column index
64701      * @return {Boolean}
64702      */
64703     isSortable : function(col){
64704         if(typeof this.config[col].sortable == "undefined"){
64705             return this.defaultSortable;
64706         }
64707         return this.config[col].sortable;
64708     },
64709
64710     /**
64711      * Returns the rendering (formatting) function defined for the column.
64712      * @param {Number} col The column index.
64713      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64714      */
64715     getRenderer : function(col){
64716         if(!this.config[col].renderer){
64717             return Roo.grid.ColumnModel.defaultRenderer;
64718         }
64719         return this.config[col].renderer;
64720     },
64721
64722     /**
64723      * Sets the rendering (formatting) function for a column.
64724      * @param {Number} col The column index
64725      * @param {Function} fn The function to use to process the cell's raw data
64726      * to return HTML markup for the grid view. The render function is called with
64727      * the following parameters:<ul>
64728      * <li>Data value.</li>
64729      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64730      * <li>css A CSS style string to apply to the table cell.</li>
64731      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
64732      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
64733      * <li>Row index</li>
64734      * <li>Column index</li>
64735      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
64736      */
64737     setRenderer : function(col, fn){
64738         this.config[col].renderer = fn;
64739     },
64740
64741     /**
64742      * Returns the width for the specified column.
64743      * @param {Number} col The column index
64744      * @param (optional) {String} gridSize bootstrap width size.
64745      * @return {Number}
64746      */
64747     getColumnWidth : function(col, gridSize)
64748         {
64749                 var cfg = this.config[col];
64750                 
64751                 if (typeof(gridSize) == 'undefined') {
64752                         return cfg.width * 1 || this.defaultWidth;
64753                 }
64754                 if (gridSize === false) { // if we set it..
64755                         return cfg.width || false;
64756                 }
64757                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
64758                 
64759                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
64760                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
64761                                 continue;
64762                         }
64763                         return cfg[ sizes[i] ];
64764                 }
64765                 return 1;
64766                 
64767     },
64768
64769     /**
64770      * Sets the width for a column.
64771      * @param {Number} col The column index
64772      * @param {Number} width The new width
64773      */
64774     setColumnWidth : function(col, width, suppressEvent){
64775         this.config[col].width = width;
64776         this.totalWidth = null;
64777         if(!suppressEvent){
64778              this.fireEvent("widthchange", this, col, width);
64779         }
64780     },
64781
64782     /**
64783      * Returns the total width of all columns.
64784      * @param {Boolean} includeHidden True to include hidden column widths
64785      * @return {Number}
64786      */
64787     getTotalWidth : function(includeHidden){
64788         if(!this.totalWidth){
64789             this.totalWidth = 0;
64790             for(var i = 0, len = this.config.length; i < len; i++){
64791                 if(includeHidden || !this.isHidden(i)){
64792                     this.totalWidth += this.getColumnWidth(i);
64793                 }
64794             }
64795         }
64796         return this.totalWidth;
64797     },
64798
64799     /**
64800      * Returns the header for the specified column.
64801      * @param {Number} col The column index
64802      * @return {String}
64803      */
64804     getColumnHeader : function(col){
64805         return this.config[col].header;
64806     },
64807
64808     /**
64809      * Sets the header for a column.
64810      * @param {Number} col The column index
64811      * @param {String} header The new header
64812      */
64813     setColumnHeader : function(col, header){
64814         this.config[col].header = header;
64815         this.fireEvent("headerchange", this, col, header);
64816     },
64817
64818     /**
64819      * Returns the tooltip for the specified column.
64820      * @param {Number} col The column index
64821      * @return {String}
64822      */
64823     getColumnTooltip : function(col){
64824             return this.config[col].tooltip;
64825     },
64826     /**
64827      * Sets the tooltip for a column.
64828      * @param {Number} col The column index
64829      * @param {String} tooltip The new tooltip
64830      */
64831     setColumnTooltip : function(col, tooltip){
64832             this.config[col].tooltip = tooltip;
64833     },
64834
64835     /**
64836      * Returns the dataIndex for the specified column.
64837      * @param {Number} col The column index
64838      * @return {Number}
64839      */
64840     getDataIndex : function(col){
64841         return this.config[col].dataIndex;
64842     },
64843
64844     /**
64845      * Sets the dataIndex for a column.
64846      * @param {Number} col The column index
64847      * @param {Number} dataIndex The new dataIndex
64848      */
64849     setDataIndex : function(col, dataIndex){
64850         this.config[col].dataIndex = dataIndex;
64851     },
64852
64853     
64854     
64855     /**
64856      * Returns true if the cell is editable.
64857      * @param {Number} colIndex The column index
64858      * @param {Number} rowIndex The row index - this is nto actually used..?
64859      * @return {Boolean}
64860      */
64861     isCellEditable : function(colIndex, rowIndex){
64862         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
64863     },
64864
64865     /**
64866      * Returns the editor defined for the cell/column.
64867      * return false or null to disable editing.
64868      * @param {Number} colIndex The column index
64869      * @param {Number} rowIndex The row index
64870      * @return {Object}
64871      */
64872     getCellEditor : function(colIndex, rowIndex){
64873         return this.config[colIndex].editor;
64874     },
64875
64876     /**
64877      * Sets if a column is editable.
64878      * @param {Number} col The column index
64879      * @param {Boolean} editable True if the column is editable
64880      */
64881     setEditable : function(col, editable){
64882         this.config[col].editable = editable;
64883     },
64884
64885
64886     /**
64887      * Returns true if the column is hidden.
64888      * @param {Number} colIndex The column index
64889      * @return {Boolean}
64890      */
64891     isHidden : function(colIndex){
64892         return this.config[colIndex].hidden;
64893     },
64894
64895
64896     /**
64897      * Returns true if the column width cannot be changed
64898      */
64899     isFixed : function(colIndex){
64900         return this.config[colIndex].fixed;
64901     },
64902
64903     /**
64904      * Returns true if the column can be resized
64905      * @return {Boolean}
64906      */
64907     isResizable : function(colIndex){
64908         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
64909     },
64910     /**
64911      * Sets if a column is hidden.
64912      * @param {Number} colIndex The column index
64913      * @param {Boolean} hidden True if the column is hidden
64914      */
64915     setHidden : function(colIndex, hidden){
64916         this.config[colIndex].hidden = hidden;
64917         this.totalWidth = null;
64918         this.fireEvent("hiddenchange", this, colIndex, hidden);
64919     },
64920
64921     /**
64922      * Sets the editor for a column.
64923      * @param {Number} col The column index
64924      * @param {Object} editor The editor object
64925      */
64926     setEditor : function(col, editor){
64927         this.config[col].editor = editor;
64928     },
64929     /**
64930      * Add a column (experimental...) - defaults to adding to the end..
64931      * @param {Object} config 
64932     */
64933     addColumn : function(c)
64934     {
64935     
64936         var i = this.config.length;
64937         this.config[i] = c;
64938         
64939         if(typeof c.dataIndex == "undefined"){
64940             c.dataIndex = i;
64941         }
64942         if(typeof c.renderer == "string"){
64943             c.renderer = Roo.util.Format[c.renderer];
64944         }
64945         if(typeof c.id == "undefined"){
64946             c.id = Roo.id();
64947         }
64948         if(c.editor && c.editor.xtype){
64949             c.editor  = Roo.factory(c.editor, Roo.grid);
64950         }
64951         if(c.editor && c.editor.isFormField){
64952             c.editor = new Roo.grid.GridEditor(c.editor);
64953         }
64954         this.lookup[c.id] = c;
64955     }
64956     
64957 });
64958
64959 Roo.grid.ColumnModel.defaultRenderer = function(value)
64960 {
64961     if(typeof value == "object") {
64962         return value;
64963     }
64964         if(typeof value == "string" && value.length < 1){
64965             return "&#160;";
64966         }
64967     
64968         return String.format("{0}", value);
64969 };
64970
64971 // Alias for backwards compatibility
64972 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
64973 /*
64974  * Based on:
64975  * Ext JS Library 1.1.1
64976  * Copyright(c) 2006-2007, Ext JS, LLC.
64977  *
64978  * Originally Released Under LGPL - original licence link has changed is not relivant.
64979  *
64980  * Fork - LGPL
64981  * <script type="text/javascript">
64982  */
64983
64984 /**
64985  * @class Roo.grid.AbstractSelectionModel
64986  * @extends Roo.util.Observable
64987  * @abstract
64988  * Abstract base class for grid SelectionModels.  It provides the interface that should be
64989  * implemented by descendant classes.  This class should not be directly instantiated.
64990  * @constructor
64991  */
64992 Roo.grid.AbstractSelectionModel = function(){
64993     this.locked = false;
64994     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
64995 };
64996
64997 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
64998     /** @ignore Called by the grid automatically. Do not call directly. */
64999     init : function(grid){
65000         this.grid = grid;
65001         this.initEvents();
65002     },
65003
65004     /**
65005      * Locks the selections.
65006      */
65007     lock : function(){
65008         this.locked = true;
65009     },
65010
65011     /**
65012      * Unlocks the selections.
65013      */
65014     unlock : function(){
65015         this.locked = false;
65016     },
65017
65018     /**
65019      * Returns true if the selections are locked.
65020      * @return {Boolean}
65021      */
65022     isLocked : function(){
65023         return this.locked;
65024     }
65025 });/*
65026  * Based on:
65027  * Ext JS Library 1.1.1
65028  * Copyright(c) 2006-2007, Ext JS, LLC.
65029  *
65030  * Originally Released Under LGPL - original licence link has changed is not relivant.
65031  *
65032  * Fork - LGPL
65033  * <script type="text/javascript">
65034  */
65035 /**
65036  * @extends Roo.grid.AbstractSelectionModel
65037  * @class Roo.grid.RowSelectionModel
65038  * The default SelectionModel used by {@link Roo.grid.Grid}.
65039  * It supports multiple selections and keyboard selection/navigation. 
65040  * @constructor
65041  * @param {Object} config
65042  */
65043 Roo.grid.RowSelectionModel = function(config){
65044     Roo.apply(this, config);
65045     this.selections = new Roo.util.MixedCollection(false, function(o){
65046         return o.id;
65047     });
65048
65049     this.last = false;
65050     this.lastActive = false;
65051
65052     this.addEvents({
65053         /**
65054         * @event selectionchange
65055         * Fires when the selection changes
65056         * @param {SelectionModel} this
65057         */
65058        "selectionchange" : true,
65059        /**
65060         * @event afterselectionchange
65061         * Fires after the selection changes (eg. by key press or clicking)
65062         * @param {SelectionModel} this
65063         */
65064        "afterselectionchange" : true,
65065        /**
65066         * @event beforerowselect
65067         * Fires when a row is selected being selected, return false to cancel.
65068         * @param {SelectionModel} this
65069         * @param {Number} rowIndex The selected index
65070         * @param {Boolean} keepExisting False if other selections will be cleared
65071         */
65072        "beforerowselect" : true,
65073        /**
65074         * @event rowselect
65075         * Fires when a row is selected.
65076         * @param {SelectionModel} this
65077         * @param {Number} rowIndex The selected index
65078         * @param {Roo.data.Record} r The record
65079         */
65080        "rowselect" : true,
65081        /**
65082         * @event rowdeselect
65083         * Fires when a row is deselected.
65084         * @param {SelectionModel} this
65085         * @param {Number} rowIndex The selected index
65086         */
65087         "rowdeselect" : true
65088     });
65089     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65090     this.locked = false;
65091 };
65092
65093 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
65094     /**
65095      * @cfg {Boolean} singleSelect
65096      * True to allow selection of only one row at a time (defaults to false)
65097      */
65098     singleSelect : false,
65099
65100     // private
65101     initEvents : function(){
65102
65103         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65104             this.grid.on("mousedown", this.handleMouseDown, this);
65105         }else{ // allow click to work like normal
65106             this.grid.on("rowclick", this.handleDragableRowClick, this);
65107         }
65108         // bootstrap does not have a view..
65109         var view = this.grid.view ? this.grid.view : this.grid;
65110         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65111             "up" : function(e){
65112                 if(!e.shiftKey){
65113                     this.selectPrevious(e.shiftKey);
65114                 }else if(this.last !== false && this.lastActive !== false){
65115                     var last = this.last;
65116                     this.selectRange(this.last,  this.lastActive-1);
65117                     view.focusRow(this.lastActive);
65118                     if(last !== false){
65119                         this.last = last;
65120                     }
65121                 }else{
65122                     this.selectFirstRow();
65123                 }
65124                 this.fireEvent("afterselectionchange", this);
65125             },
65126             "down" : function(e){
65127                 if(!e.shiftKey){
65128                     this.selectNext(e.shiftKey);
65129                 }else if(this.last !== false && this.lastActive !== false){
65130                     var last = this.last;
65131                     this.selectRange(this.last,  this.lastActive+1);
65132                     view.focusRow(this.lastActive);
65133                     if(last !== false){
65134                         this.last = last;
65135                     }
65136                 }else{
65137                     this.selectFirstRow();
65138                 }
65139                 this.fireEvent("afterselectionchange", this);
65140             },
65141             scope: this
65142         });
65143
65144          
65145         view.on("refresh", this.onRefresh, this);
65146         view.on("rowupdated", this.onRowUpdated, this);
65147         view.on("rowremoved", this.onRemove, this);
65148     },
65149
65150     // private
65151     onRefresh : function(){
65152         var ds = this.grid.ds, i, v = this.grid.view;
65153         var s = this.selections;
65154         s.each(function(r){
65155             if((i = ds.indexOfId(r.id)) != -1){
65156                 v.onRowSelect(i);
65157                 s.add(ds.getAt(i)); // updating the selection relate data
65158             }else{
65159                 s.remove(r);
65160             }
65161         });
65162     },
65163
65164     // private
65165     onRemove : function(v, index, r){
65166         this.selections.remove(r);
65167     },
65168
65169     // private
65170     onRowUpdated : function(v, index, r){
65171         if(this.isSelected(r)){
65172             v.onRowSelect(index);
65173         }
65174     },
65175
65176     /**
65177      * Select records.
65178      * @param {Array} records The records to select
65179      * @param {Boolean} keepExisting (optional) True to keep existing selections
65180      */
65181     selectRecords : function(records, keepExisting){
65182         if(!keepExisting){
65183             this.clearSelections();
65184         }
65185         var ds = this.grid.ds;
65186         for(var i = 0, len = records.length; i < len; i++){
65187             this.selectRow(ds.indexOf(records[i]), true);
65188         }
65189     },
65190
65191     /**
65192      * Gets the number of selected rows.
65193      * @return {Number}
65194      */
65195     getCount : function(){
65196         return this.selections.length;
65197     },
65198
65199     /**
65200      * Selects the first row in the grid.
65201      */
65202     selectFirstRow : function(){
65203         this.selectRow(0);
65204     },
65205
65206     /**
65207      * Select the last row.
65208      * @param {Boolean} keepExisting (optional) True to keep existing selections
65209      */
65210     selectLastRow : function(keepExisting){
65211         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65212     },
65213
65214     /**
65215      * Selects the row immediately following the last selected row.
65216      * @param {Boolean} keepExisting (optional) True to keep existing selections
65217      */
65218     selectNext : function(keepExisting){
65219         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65220             this.selectRow(this.last+1, keepExisting);
65221             var view = this.grid.view ? this.grid.view : this.grid;
65222             view.focusRow(this.last);
65223         }
65224     },
65225
65226     /**
65227      * Selects the row that precedes the last selected row.
65228      * @param {Boolean} keepExisting (optional) True to keep existing selections
65229      */
65230     selectPrevious : function(keepExisting){
65231         if(this.last){
65232             this.selectRow(this.last-1, keepExisting);
65233             var view = this.grid.view ? this.grid.view : this.grid;
65234             view.focusRow(this.last);
65235         }
65236     },
65237
65238     /**
65239      * Returns the selected records
65240      * @return {Array} Array of selected records
65241      */
65242     getSelections : function(){
65243         return [].concat(this.selections.items);
65244     },
65245
65246     /**
65247      * Returns the first selected record.
65248      * @return {Record}
65249      */
65250     getSelected : function(){
65251         return this.selections.itemAt(0);
65252     },
65253
65254
65255     /**
65256      * Clears all selections.
65257      */
65258     clearSelections : function(fast){
65259         if(this.locked) {
65260             return;
65261         }
65262         if(fast !== true){
65263             var ds = this.grid.ds;
65264             var s = this.selections;
65265             s.each(function(r){
65266                 this.deselectRow(ds.indexOfId(r.id));
65267             }, this);
65268             s.clear();
65269         }else{
65270             this.selections.clear();
65271         }
65272         this.last = false;
65273     },
65274
65275
65276     /**
65277      * Selects all rows.
65278      */
65279     selectAll : function(){
65280         if(this.locked) {
65281             return;
65282         }
65283         this.selections.clear();
65284         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65285             this.selectRow(i, true);
65286         }
65287     },
65288
65289     /**
65290      * Returns True if there is a selection.
65291      * @return {Boolean}
65292      */
65293     hasSelection : function(){
65294         return this.selections.length > 0;
65295     },
65296
65297     /**
65298      * Returns True if the specified row is selected.
65299      * @param {Number/Record} record The record or index of the record to check
65300      * @return {Boolean}
65301      */
65302     isSelected : function(index){
65303         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65304         return (r && this.selections.key(r.id) ? true : false);
65305     },
65306
65307     /**
65308      * Returns True if the specified record id is selected.
65309      * @param {String} id The id of record to check
65310      * @return {Boolean}
65311      */
65312     isIdSelected : function(id){
65313         return (this.selections.key(id) ? true : false);
65314     },
65315
65316     // private
65317     handleMouseDown : function(e, t)
65318     {
65319         var view = this.grid.view ? this.grid.view : this.grid;
65320         var rowIndex;
65321         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65322             return;
65323         };
65324         if(e.shiftKey && this.last !== false){
65325             var last = this.last;
65326             this.selectRange(last, rowIndex, e.ctrlKey);
65327             this.last = last; // reset the last
65328             view.focusRow(rowIndex);
65329         }else{
65330             var isSelected = this.isSelected(rowIndex);
65331             if(e.button !== 0 && isSelected){
65332                 view.focusRow(rowIndex);
65333             }else if(e.ctrlKey && isSelected){
65334                 this.deselectRow(rowIndex);
65335             }else if(!isSelected){
65336                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65337                 view.focusRow(rowIndex);
65338             }
65339         }
65340         this.fireEvent("afterselectionchange", this);
65341     },
65342     // private
65343     handleDragableRowClick :  function(grid, rowIndex, e) 
65344     {
65345         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65346             this.selectRow(rowIndex, false);
65347             var view = this.grid.view ? this.grid.view : this.grid;
65348             view.focusRow(rowIndex);
65349              this.fireEvent("afterselectionchange", this);
65350         }
65351     },
65352     
65353     /**
65354      * Selects multiple rows.
65355      * @param {Array} rows Array of the indexes of the row to select
65356      * @param {Boolean} keepExisting (optional) True to keep existing selections
65357      */
65358     selectRows : function(rows, keepExisting){
65359         if(!keepExisting){
65360             this.clearSelections();
65361         }
65362         for(var i = 0, len = rows.length; i < len; i++){
65363             this.selectRow(rows[i], true);
65364         }
65365     },
65366
65367     /**
65368      * Selects a range of rows. All rows in between startRow and endRow are also selected.
65369      * @param {Number} startRow The index of the first row in the range
65370      * @param {Number} endRow The index of the last row in the range
65371      * @param {Boolean} keepExisting (optional) True to retain existing selections
65372      */
65373     selectRange : function(startRow, endRow, keepExisting){
65374         if(this.locked) {
65375             return;
65376         }
65377         if(!keepExisting){
65378             this.clearSelections();
65379         }
65380         if(startRow <= endRow){
65381             for(var i = startRow; i <= endRow; i++){
65382                 this.selectRow(i, true);
65383             }
65384         }else{
65385             for(var i = startRow; i >= endRow; i--){
65386                 this.selectRow(i, true);
65387             }
65388         }
65389     },
65390
65391     /**
65392      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65393      * @param {Number} startRow The index of the first row in the range
65394      * @param {Number} endRow The index of the last row in the range
65395      */
65396     deselectRange : function(startRow, endRow, preventViewNotify){
65397         if(this.locked) {
65398             return;
65399         }
65400         for(var i = startRow; i <= endRow; i++){
65401             this.deselectRow(i, preventViewNotify);
65402         }
65403     },
65404
65405     /**
65406      * Selects a row.
65407      * @param {Number} row The index of the row to select
65408      * @param {Boolean} keepExisting (optional) True to keep existing selections
65409      */
65410     selectRow : function(index, keepExisting, preventViewNotify){
65411         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65412             return;
65413         }
65414         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65415             if(!keepExisting || this.singleSelect){
65416                 this.clearSelections();
65417             }
65418             var r = this.grid.ds.getAt(index);
65419             this.selections.add(r);
65420             this.last = this.lastActive = index;
65421             if(!preventViewNotify){
65422                 var view = this.grid.view ? this.grid.view : this.grid;
65423                 view.onRowSelect(index);
65424             }
65425             this.fireEvent("rowselect", this, index, r);
65426             this.fireEvent("selectionchange", this);
65427         }
65428     },
65429
65430     /**
65431      * Deselects a row.
65432      * @param {Number} row The index of the row to deselect
65433      */
65434     deselectRow : function(index, preventViewNotify){
65435         if(this.locked) {
65436             return;
65437         }
65438         if(this.last == index){
65439             this.last = false;
65440         }
65441         if(this.lastActive == index){
65442             this.lastActive = false;
65443         }
65444         var r = this.grid.ds.getAt(index);
65445         this.selections.remove(r);
65446         if(!preventViewNotify){
65447             var view = this.grid.view ? this.grid.view : this.grid;
65448             view.onRowDeselect(index);
65449         }
65450         this.fireEvent("rowdeselect", this, index);
65451         this.fireEvent("selectionchange", this);
65452     },
65453
65454     // private
65455     restoreLast : function(){
65456         if(this._last){
65457             this.last = this._last;
65458         }
65459     },
65460
65461     // private
65462     acceptsNav : function(row, col, cm){
65463         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65464     },
65465
65466     // private
65467     onEditorKey : function(field, e){
65468         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65469         if(k == e.TAB){
65470             e.stopEvent();
65471             ed.completeEdit();
65472             if(e.shiftKey){
65473                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65474             }else{
65475                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65476             }
65477         }else if(k == e.ENTER && !e.ctrlKey){
65478             e.stopEvent();
65479             ed.completeEdit();
65480             if(e.shiftKey){
65481                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65482             }else{
65483                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65484             }
65485         }else if(k == e.ESC){
65486             ed.cancelEdit();
65487         }
65488         if(newCell){
65489             g.startEditing(newCell[0], newCell[1]);
65490         }
65491     }
65492 });/*
65493  * Based on:
65494  * Ext JS Library 1.1.1
65495  * Copyright(c) 2006-2007, Ext JS, LLC.
65496  *
65497  * Originally Released Under LGPL - original licence link has changed is not relivant.
65498  *
65499  * Fork - LGPL
65500  * <script type="text/javascript">
65501  */
65502 /**
65503  * @class Roo.grid.CellSelectionModel
65504  * @extends Roo.grid.AbstractSelectionModel
65505  * This class provides the basic implementation for cell selection in a grid.
65506  * @constructor
65507  * @param {Object} config The object containing the configuration of this model.
65508  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65509  */
65510 Roo.grid.CellSelectionModel = function(config){
65511     Roo.apply(this, config);
65512
65513     this.selection = null;
65514
65515     this.addEvents({
65516         /**
65517              * @event beforerowselect
65518              * Fires before a cell is selected.
65519              * @param {SelectionModel} this
65520              * @param {Number} rowIndex The selected row index
65521              * @param {Number} colIndex The selected cell index
65522              */
65523             "beforecellselect" : true,
65524         /**
65525              * @event cellselect
65526              * Fires when a cell is selected.
65527              * @param {SelectionModel} this
65528              * @param {Number} rowIndex The selected row index
65529              * @param {Number} colIndex The selected cell index
65530              */
65531             "cellselect" : true,
65532         /**
65533              * @event selectionchange
65534              * Fires when the active selection changes.
65535              * @param {SelectionModel} this
65536              * @param {Object} selection null for no selection or an object (o) with two properties
65537                 <ul>
65538                 <li>o.record: the record object for the row the selection is in</li>
65539                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65540                 </ul>
65541              */
65542             "selectionchange" : true,
65543         /**
65544              * @event tabend
65545              * Fires when the tab (or enter) was pressed on the last editable cell
65546              * You can use this to trigger add new row.
65547              * @param {SelectionModel} this
65548              */
65549             "tabend" : true,
65550          /**
65551              * @event beforeeditnext
65552              * Fires before the next editable sell is made active
65553              * You can use this to skip to another cell or fire the tabend
65554              *    if you set cell to false
65555              * @param {Object} eventdata object : { cell : [ row, col ] } 
65556              */
65557             "beforeeditnext" : true
65558     });
65559     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65560 };
65561
65562 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
65563     
65564     enter_is_tab: false,
65565
65566     /** @ignore */
65567     initEvents : function(){
65568         this.grid.on("mousedown", this.handleMouseDown, this);
65569         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65570         var view = this.grid.view;
65571         view.on("refresh", this.onViewChange, this);
65572         view.on("rowupdated", this.onRowUpdated, this);
65573         view.on("beforerowremoved", this.clearSelections, this);
65574         view.on("beforerowsinserted", this.clearSelections, this);
65575         if(this.grid.isEditor){
65576             this.grid.on("beforeedit", this.beforeEdit,  this);
65577         }
65578     },
65579
65580         //private
65581     beforeEdit : function(e){
65582         this.select(e.row, e.column, false, true, e.record);
65583     },
65584
65585         //private
65586     onRowUpdated : function(v, index, r){
65587         if(this.selection && this.selection.record == r){
65588             v.onCellSelect(index, this.selection.cell[1]);
65589         }
65590     },
65591
65592         //private
65593     onViewChange : function(){
65594         this.clearSelections(true);
65595     },
65596
65597         /**
65598          * Returns the currently selected cell,.
65599          * @return {Array} The selected cell (row, column) or null if none selected.
65600          */
65601     getSelectedCell : function(){
65602         return this.selection ? this.selection.cell : null;
65603     },
65604
65605     /**
65606      * Clears all selections.
65607      * @param {Boolean} true to prevent the gridview from being notified about the change.
65608      */
65609     clearSelections : function(preventNotify){
65610         var s = this.selection;
65611         if(s){
65612             if(preventNotify !== true){
65613                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65614             }
65615             this.selection = null;
65616             this.fireEvent("selectionchange", this, null);
65617         }
65618     },
65619
65620     /**
65621      * Returns true if there is a selection.
65622      * @return {Boolean}
65623      */
65624     hasSelection : function(){
65625         return this.selection ? true : false;
65626     },
65627
65628     /** @ignore */
65629     handleMouseDown : function(e, t){
65630         var v = this.grid.getView();
65631         if(this.isLocked()){
65632             return;
65633         };
65634         var row = v.findRowIndex(t);
65635         var cell = v.findCellIndex(t);
65636         if(row !== false && cell !== false){
65637             this.select(row, cell);
65638         }
65639     },
65640
65641     /**
65642      * Selects a cell.
65643      * @param {Number} rowIndex
65644      * @param {Number} collIndex
65645      */
65646     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65647         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65648             this.clearSelections();
65649             r = r || this.grid.dataSource.getAt(rowIndex);
65650             this.selection = {
65651                 record : r,
65652                 cell : [rowIndex, colIndex]
65653             };
65654             if(!preventViewNotify){
65655                 var v = this.grid.getView();
65656                 v.onCellSelect(rowIndex, colIndex);
65657                 if(preventFocus !== true){
65658                     v.focusCell(rowIndex, colIndex);
65659                 }
65660             }
65661             this.fireEvent("cellselect", this, rowIndex, colIndex);
65662             this.fireEvent("selectionchange", this, this.selection);
65663         }
65664     },
65665
65666         //private
65667     isSelectable : function(rowIndex, colIndex, cm){
65668         return !cm.isHidden(colIndex);
65669     },
65670
65671     /** @ignore */
65672     handleKeyDown : function(e){
65673         //Roo.log('Cell Sel Model handleKeyDown');
65674         if(!e.isNavKeyPress()){
65675             return;
65676         }
65677         var g = this.grid, s = this.selection;
65678         if(!s){
65679             e.stopEvent();
65680             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
65681             if(cell){
65682                 this.select(cell[0], cell[1]);
65683             }
65684             return;
65685         }
65686         var sm = this;
65687         var walk = function(row, col, step){
65688             return g.walkCells(row, col, step, sm.isSelectable,  sm);
65689         };
65690         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65691         var newCell;
65692
65693       
65694
65695         switch(k){
65696             case e.TAB:
65697                 // handled by onEditorKey
65698                 if (g.isEditor && g.editing) {
65699                     return;
65700                 }
65701                 if(e.shiftKey) {
65702                     newCell = walk(r, c-1, -1);
65703                 } else {
65704                     newCell = walk(r, c+1, 1);
65705                 }
65706                 break;
65707             
65708             case e.DOWN:
65709                newCell = walk(r+1, c, 1);
65710                 break;
65711             
65712             case e.UP:
65713                 newCell = walk(r-1, c, -1);
65714                 break;
65715             
65716             case e.RIGHT:
65717                 newCell = walk(r, c+1, 1);
65718                 break;
65719             
65720             case e.LEFT:
65721                 newCell = walk(r, c-1, -1);
65722                 break;
65723             
65724             case e.ENTER:
65725                 
65726                 if(g.isEditor && !g.editing){
65727                    g.startEditing(r, c);
65728                    e.stopEvent();
65729                    return;
65730                 }
65731                 
65732                 
65733              break;
65734         };
65735         if(newCell){
65736             this.select(newCell[0], newCell[1]);
65737             e.stopEvent();
65738             
65739         }
65740     },
65741
65742     acceptsNav : function(row, col, cm){
65743         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65744     },
65745     /**
65746      * Selects a cell.
65747      * @param {Number} field (not used) - as it's normally used as a listener
65748      * @param {Number} e - event - fake it by using
65749      *
65750      * var e = Roo.EventObjectImpl.prototype;
65751      * e.keyCode = e.TAB
65752      *
65753      * 
65754      */
65755     onEditorKey : function(field, e){
65756         
65757         var k = e.getKey(),
65758             newCell,
65759             g = this.grid,
65760             ed = g.activeEditor,
65761             forward = false;
65762         ///Roo.log('onEditorKey' + k);
65763         
65764         
65765         if (this.enter_is_tab && k == e.ENTER) {
65766             k = e.TAB;
65767         }
65768         
65769         if(k == e.TAB){
65770             if(e.shiftKey){
65771                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65772             }else{
65773                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65774                 forward = true;
65775             }
65776             
65777             e.stopEvent();
65778             
65779         } else if(k == e.ENTER &&  !e.ctrlKey){
65780             ed.completeEdit();
65781             e.stopEvent();
65782             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65783         
65784                 } else if(k == e.ESC){
65785             ed.cancelEdit();
65786         }
65787                 
65788         if (newCell) {
65789             var ecall = { cell : newCell, forward : forward };
65790             this.fireEvent('beforeeditnext', ecall );
65791             newCell = ecall.cell;
65792                         forward = ecall.forward;
65793         }
65794                 
65795         if(newCell){
65796             //Roo.log('next cell after edit');
65797             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
65798         } else if (forward) {
65799             // tabbed past last
65800             this.fireEvent.defer(100, this, ['tabend',this]);
65801         }
65802     }
65803 });/*
65804  * Based on:
65805  * Ext JS Library 1.1.1
65806  * Copyright(c) 2006-2007, Ext JS, LLC.
65807  *
65808  * Originally Released Under LGPL - original licence link has changed is not relivant.
65809  *
65810  * Fork - LGPL
65811  * <script type="text/javascript">
65812  */
65813  
65814 /**
65815  * @class Roo.grid.EditorGrid
65816  * @extends Roo.grid.Grid
65817  * Class for creating and editable grid.
65818  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
65819  * The container MUST have some type of size defined for the grid to fill. The container will be 
65820  * automatically set to position relative if it isn't already.
65821  * @param {Object} dataSource The data model to bind to
65822  * @param {Object} colModel The column model with info about this grid's columns
65823  */
65824 Roo.grid.EditorGrid = function(container, config){
65825     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
65826     this.getGridEl().addClass("xedit-grid");
65827
65828     if(!this.selModel){
65829         this.selModel = new Roo.grid.CellSelectionModel();
65830     }
65831
65832     this.activeEditor = null;
65833
65834         this.addEvents({
65835             /**
65836              * @event beforeedit
65837              * Fires before cell editing is triggered. The edit event object has the following properties <br />
65838              * <ul style="padding:5px;padding-left:16px;">
65839              * <li>grid - This grid</li>
65840              * <li>record - The record being edited</li>
65841              * <li>field - The field name being edited</li>
65842              * <li>value - The value for the field being edited.</li>
65843              * <li>row - The grid row index</li>
65844              * <li>column - The grid column index</li>
65845              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65846              * </ul>
65847              * @param {Object} e An edit event (see above for description)
65848              */
65849             "beforeedit" : true,
65850             /**
65851              * @event afteredit
65852              * Fires after a cell is edited. <br />
65853              * <ul style="padding:5px;padding-left:16px;">
65854              * <li>grid - This grid</li>
65855              * <li>record - The record being edited</li>
65856              * <li>field - The field name being edited</li>
65857              * <li>value - The value being set</li>
65858              * <li>originalValue - The original value for the field, before the edit.</li>
65859              * <li>row - The grid row index</li>
65860              * <li>column - The grid column index</li>
65861              * </ul>
65862              * @param {Object} e An edit event (see above for description)
65863              */
65864             "afteredit" : true,
65865             /**
65866              * @event validateedit
65867              * Fires after a cell is edited, but before the value is set in the record. 
65868          * You can use this to modify the value being set in the field, Return false
65869              * to cancel the change. The edit event object has the following properties <br />
65870              * <ul style="padding:5px;padding-left:16px;">
65871          * <li>editor - This editor</li>
65872              * <li>grid - This grid</li>
65873              * <li>record - The record being edited</li>
65874              * <li>field - The field name being edited</li>
65875              * <li>value - The value being set</li>
65876              * <li>originalValue - The original value for the field, before the edit.</li>
65877              * <li>row - The grid row index</li>
65878              * <li>column - The grid column index</li>
65879              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65880              * </ul>
65881              * @param {Object} e An edit event (see above for description)
65882              */
65883             "validateedit" : true
65884         });
65885     this.on("bodyscroll", this.stopEditing,  this);
65886     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
65887 };
65888
65889 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
65890     /**
65891      * @cfg {Number} clicksToEdit
65892      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
65893      */
65894     clicksToEdit: 2,
65895
65896     // private
65897     isEditor : true,
65898     // private
65899     trackMouseOver: false, // causes very odd FF errors
65900
65901     onCellDblClick : function(g, row, col){
65902         this.startEditing(row, col);
65903     },
65904
65905     onEditComplete : function(ed, value, startValue){
65906         this.editing = false;
65907         this.activeEditor = null;
65908         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
65909         var r = ed.record;
65910         var field = this.colModel.getDataIndex(ed.col);
65911         var e = {
65912             grid: this,
65913             record: r,
65914             field: field,
65915             originalValue: startValue,
65916             value: value,
65917             row: ed.row,
65918             column: ed.col,
65919             cancel:false,
65920             editor: ed
65921         };
65922         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
65923         cell.show();
65924           
65925         if(String(value) !== String(startValue)){
65926             
65927             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
65928                 r.set(field, e.value);
65929                 // if we are dealing with a combo box..
65930                 // then we also set the 'name' colum to be the displayField
65931                 if (ed.field.displayField && ed.field.name) {
65932                     r.set(ed.field.name, ed.field.el.dom.value);
65933                 }
65934                 
65935                 delete e.cancel; //?? why!!!
65936                 this.fireEvent("afteredit", e);
65937             }
65938         } else {
65939             this.fireEvent("afteredit", e); // always fire it!
65940         }
65941         this.view.focusCell(ed.row, ed.col);
65942     },
65943
65944     /**
65945      * Starts editing the specified for the specified row/column
65946      * @param {Number} rowIndex
65947      * @param {Number} colIndex
65948      */
65949     startEditing : function(row, col){
65950         this.stopEditing();
65951         if(this.colModel.isCellEditable(col, row)){
65952             this.view.ensureVisible(row, col, true);
65953           
65954             var r = this.dataSource.getAt(row);
65955             var field = this.colModel.getDataIndex(col);
65956             var cell = Roo.get(this.view.getCell(row,col));
65957             var e = {
65958                 grid: this,
65959                 record: r,
65960                 field: field,
65961                 value: r.data[field],
65962                 row: row,
65963                 column: col,
65964                 cancel:false 
65965             };
65966             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
65967                 this.editing = true;
65968                 var ed = this.colModel.getCellEditor(col, row);
65969                 
65970                 if (!ed) {
65971                     return;
65972                 }
65973                 if(!ed.rendered){
65974                     ed.render(ed.parentEl || document.body);
65975                 }
65976                 ed.field.reset();
65977                
65978                 cell.hide();
65979                 
65980                 (function(){ // complex but required for focus issues in safari, ie and opera
65981                     ed.row = row;
65982                     ed.col = col;
65983                     ed.record = r;
65984                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
65985                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
65986                     this.activeEditor = ed;
65987                     var v = r.data[field];
65988                     ed.startEdit(this.view.getCell(row, col), v);
65989                     // combo's with 'displayField and name set
65990                     if (ed.field.displayField && ed.field.name) {
65991                         ed.field.el.dom.value = r.data[ed.field.name];
65992                     }
65993                     
65994                     
65995                 }).defer(50, this);
65996             }
65997         }
65998     },
65999         
66000     /**
66001      * Stops any active editing
66002      */
66003     stopEditing : function(){
66004         if(this.activeEditor){
66005             this.activeEditor.completeEdit();
66006         }
66007         this.activeEditor = null;
66008     },
66009         
66010          /**
66011      * Called to get grid's drag proxy text, by default returns this.ddText.
66012      * @return {String}
66013      */
66014     getDragDropText : function(){
66015         var count = this.selModel.getSelectedCell() ? 1 : 0;
66016         return String.format(this.ddText, count, count == 1 ? '' : 's');
66017     }
66018         
66019 });/*
66020  * Based on:
66021  * Ext JS Library 1.1.1
66022  * Copyright(c) 2006-2007, Ext JS, LLC.
66023  *
66024  * Originally Released Under LGPL - original licence link has changed is not relivant.
66025  *
66026  * Fork - LGPL
66027  * <script type="text/javascript">
66028  */
66029
66030 // private - not really -- you end up using it !
66031 // This is a support class used internally by the Grid components
66032
66033 /**
66034  * @class Roo.grid.GridEditor
66035  * @extends Roo.Editor
66036  * Class for creating and editable grid elements.
66037  * @param {Object} config any settings (must include field)
66038  */
66039 Roo.grid.GridEditor = function(field, config){
66040     if (!config && field.field) {
66041         config = field;
66042         field = Roo.factory(config.field, Roo.form);
66043     }
66044     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66045     field.monitorTab = false;
66046 };
66047
66048 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66049     
66050     /**
66051      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66052      */
66053     
66054     alignment: "tl-tl",
66055     autoSize: "width",
66056     hideEl : false,
66057     cls: "x-small-editor x-grid-editor",
66058     shim:false,
66059     shadow:"frame"
66060 });/*
66061  * Based on:
66062  * Ext JS Library 1.1.1
66063  * Copyright(c) 2006-2007, Ext JS, LLC.
66064  *
66065  * Originally Released Under LGPL - original licence link has changed is not relivant.
66066  *
66067  * Fork - LGPL
66068  * <script type="text/javascript">
66069  */
66070   
66071
66072   
66073 Roo.grid.PropertyRecord = Roo.data.Record.create([
66074     {name:'name',type:'string'},  'value'
66075 ]);
66076
66077
66078 Roo.grid.PropertyStore = function(grid, source){
66079     this.grid = grid;
66080     this.store = new Roo.data.Store({
66081         recordType : Roo.grid.PropertyRecord
66082     });
66083     this.store.on('update', this.onUpdate,  this);
66084     if(source){
66085         this.setSource(source);
66086     }
66087     Roo.grid.PropertyStore.superclass.constructor.call(this);
66088 };
66089
66090
66091
66092 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66093     setSource : function(o){
66094         this.source = o;
66095         this.store.removeAll();
66096         var data = [];
66097         for(var k in o){
66098             if(this.isEditableValue(o[k])){
66099                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66100             }
66101         }
66102         this.store.loadRecords({records: data}, {}, true);
66103     },
66104
66105     onUpdate : function(ds, record, type){
66106         if(type == Roo.data.Record.EDIT){
66107             var v = record.data['value'];
66108             var oldValue = record.modified['value'];
66109             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66110                 this.source[record.id] = v;
66111                 record.commit();
66112                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66113             }else{
66114                 record.reject();
66115             }
66116         }
66117     },
66118
66119     getProperty : function(row){
66120        return this.store.getAt(row);
66121     },
66122
66123     isEditableValue: function(val){
66124         if(val && val instanceof Date){
66125             return true;
66126         }else if(typeof val == 'object' || typeof val == 'function'){
66127             return false;
66128         }
66129         return true;
66130     },
66131
66132     setValue : function(prop, value){
66133         this.source[prop] = value;
66134         this.store.getById(prop).set('value', value);
66135     },
66136
66137     getSource : function(){
66138         return this.source;
66139     }
66140 });
66141
66142 Roo.grid.PropertyColumnModel = function(grid, store){
66143     this.grid = grid;
66144     var g = Roo.grid;
66145     g.PropertyColumnModel.superclass.constructor.call(this, [
66146         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66147         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66148     ]);
66149     this.store = store;
66150     this.bselect = Roo.DomHelper.append(document.body, {
66151         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66152             {tag: 'option', value: 'true', html: 'true'},
66153             {tag: 'option', value: 'false', html: 'false'}
66154         ]
66155     });
66156     Roo.id(this.bselect);
66157     var f = Roo.form;
66158     this.editors = {
66159         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66160         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66161         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66162         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66163         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66164     };
66165     this.renderCellDelegate = this.renderCell.createDelegate(this);
66166     this.renderPropDelegate = this.renderProp.createDelegate(this);
66167 };
66168
66169 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66170     
66171     
66172     nameText : 'Name',
66173     valueText : 'Value',
66174     
66175     dateFormat : 'm/j/Y',
66176     
66177     
66178     renderDate : function(dateVal){
66179         return dateVal.dateFormat(this.dateFormat);
66180     },
66181
66182     renderBool : function(bVal){
66183         return bVal ? 'true' : 'false';
66184     },
66185
66186     isCellEditable : function(colIndex, rowIndex){
66187         return colIndex == 1;
66188     },
66189
66190     getRenderer : function(col){
66191         return col == 1 ?
66192             this.renderCellDelegate : this.renderPropDelegate;
66193     },
66194
66195     renderProp : function(v){
66196         return this.getPropertyName(v);
66197     },
66198
66199     renderCell : function(val){
66200         var rv = val;
66201         if(val instanceof Date){
66202             rv = this.renderDate(val);
66203         }else if(typeof val == 'boolean'){
66204             rv = this.renderBool(val);
66205         }
66206         return Roo.util.Format.htmlEncode(rv);
66207     },
66208
66209     getPropertyName : function(name){
66210         var pn = this.grid.propertyNames;
66211         return pn && pn[name] ? pn[name] : name;
66212     },
66213
66214     getCellEditor : function(colIndex, rowIndex){
66215         var p = this.store.getProperty(rowIndex);
66216         var n = p.data['name'], val = p.data['value'];
66217         
66218         if(typeof(this.grid.customEditors[n]) == 'string'){
66219             return this.editors[this.grid.customEditors[n]];
66220         }
66221         if(typeof(this.grid.customEditors[n]) != 'undefined'){
66222             return this.grid.customEditors[n];
66223         }
66224         if(val instanceof Date){
66225             return this.editors['date'];
66226         }else if(typeof val == 'number'){
66227             return this.editors['number'];
66228         }else if(typeof val == 'boolean'){
66229             return this.editors['boolean'];
66230         }else{
66231             return this.editors['string'];
66232         }
66233     }
66234 });
66235
66236 /**
66237  * @class Roo.grid.PropertyGrid
66238  * @extends Roo.grid.EditorGrid
66239  * This class represents the  interface of a component based property grid control.
66240  * <br><br>Usage:<pre><code>
66241  var grid = new Roo.grid.PropertyGrid("my-container-id", {
66242       
66243  });
66244  // set any options
66245  grid.render();
66246  * </code></pre>
66247   
66248  * @constructor
66249  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66250  * The container MUST have some type of size defined for the grid to fill. The container will be
66251  * automatically set to position relative if it isn't already.
66252  * @param {Object} config A config object that sets properties on this grid.
66253  */
66254 Roo.grid.PropertyGrid = function(container, config){
66255     config = config || {};
66256     var store = new Roo.grid.PropertyStore(this);
66257     this.store = store;
66258     var cm = new Roo.grid.PropertyColumnModel(this, store);
66259     store.store.sort('name', 'ASC');
66260     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66261         ds: store.store,
66262         cm: cm,
66263         enableColLock:false,
66264         enableColumnMove:false,
66265         stripeRows:false,
66266         trackMouseOver: false,
66267         clicksToEdit:1
66268     }, config));
66269     this.getGridEl().addClass('x-props-grid');
66270     this.lastEditRow = null;
66271     this.on('columnresize', this.onColumnResize, this);
66272     this.addEvents({
66273          /**
66274              * @event beforepropertychange
66275              * Fires before a property changes (return false to stop?)
66276              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66277              * @param {String} id Record Id
66278              * @param {String} newval New Value
66279          * @param {String} oldval Old Value
66280              */
66281         "beforepropertychange": true,
66282         /**
66283              * @event propertychange
66284              * Fires after a property changes
66285              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66286              * @param {String} id Record Id
66287              * @param {String} newval New Value
66288          * @param {String} oldval Old Value
66289              */
66290         "propertychange": true
66291     });
66292     this.customEditors = this.customEditors || {};
66293 };
66294 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66295     
66296      /**
66297      * @cfg {Object} customEditors map of colnames=> custom editors.
66298      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66299      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66300      * false disables editing of the field.
66301          */
66302     
66303       /**
66304      * @cfg {Object} propertyNames map of property Names to their displayed value
66305          */
66306     
66307     render : function(){
66308         Roo.grid.PropertyGrid.superclass.render.call(this);
66309         this.autoSize.defer(100, this);
66310     },
66311
66312     autoSize : function(){
66313         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66314         if(this.view){
66315             this.view.fitColumns();
66316         }
66317     },
66318
66319     onColumnResize : function(){
66320         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66321         this.autoSize();
66322     },
66323     /**
66324      * Sets the data for the Grid
66325      * accepts a Key => Value object of all the elements avaiable.
66326      * @param {Object} data  to appear in grid.
66327      */
66328     setSource : function(source){
66329         this.store.setSource(source);
66330         //this.autoSize();
66331     },
66332     /**
66333      * Gets all the data from the grid.
66334      * @return {Object} data  data stored in grid
66335      */
66336     getSource : function(){
66337         return this.store.getSource();
66338     }
66339 });/*
66340   
66341  * Licence LGPL
66342  
66343  */
66344  
66345 /**
66346  * @class Roo.grid.Calendar
66347  * @extends Roo.grid.Grid
66348  * This class extends the Grid to provide a calendar widget
66349  * <br><br>Usage:<pre><code>
66350  var grid = new Roo.grid.Calendar("my-container-id", {
66351      ds: myDataStore,
66352      cm: myColModel,
66353      selModel: mySelectionModel,
66354      autoSizeColumns: true,
66355      monitorWindowResize: false,
66356      trackMouseOver: true
66357      eventstore : real data store..
66358  });
66359  // set any options
66360  grid.render();
66361   
66362   * @constructor
66363  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66364  * The container MUST have some type of size defined for the grid to fill. The container will be
66365  * automatically set to position relative if it isn't already.
66366  * @param {Object} config A config object that sets properties on this grid.
66367  */
66368 Roo.grid.Calendar = function(container, config){
66369         // initialize the container
66370         this.container = Roo.get(container);
66371         this.container.update("");
66372         this.container.setStyle("overflow", "hidden");
66373     this.container.addClass('x-grid-container');
66374
66375     this.id = this.container.id;
66376
66377     Roo.apply(this, config);
66378     // check and correct shorthanded configs
66379     
66380     var rows = [];
66381     var d =1;
66382     for (var r = 0;r < 6;r++) {
66383         
66384         rows[r]=[];
66385         for (var c =0;c < 7;c++) {
66386             rows[r][c]= '';
66387         }
66388     }
66389     if (this.eventStore) {
66390         this.eventStore= Roo.factory(this.eventStore, Roo.data);
66391         this.eventStore.on('load',this.onLoad, this);
66392         this.eventStore.on('beforeload',this.clearEvents, this);
66393          
66394     }
66395     
66396     this.dataSource = new Roo.data.Store({
66397             proxy: new Roo.data.MemoryProxy(rows),
66398             reader: new Roo.data.ArrayReader({}, [
66399                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66400     });
66401
66402     this.dataSource.load();
66403     this.ds = this.dataSource;
66404     this.ds.xmodule = this.xmodule || false;
66405     
66406     
66407     var cellRender = function(v,x,r)
66408     {
66409         return String.format(
66410             '<div class="fc-day  fc-widget-content"><div>' +
66411                 '<div class="fc-event-container"></div>' +
66412                 '<div class="fc-day-number">{0}</div>'+
66413                 
66414                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66415             '</div></div>', v);
66416     
66417     }
66418     
66419     
66420     this.colModel = new Roo.grid.ColumnModel( [
66421         {
66422             xtype: 'ColumnModel',
66423             xns: Roo.grid,
66424             dataIndex : 'weekday0',
66425             header : 'Sunday',
66426             renderer : cellRender
66427         },
66428         {
66429             xtype: 'ColumnModel',
66430             xns: Roo.grid,
66431             dataIndex : 'weekday1',
66432             header : 'Monday',
66433             renderer : cellRender
66434         },
66435         {
66436             xtype: 'ColumnModel',
66437             xns: Roo.grid,
66438             dataIndex : 'weekday2',
66439             header : 'Tuesday',
66440             renderer : cellRender
66441         },
66442         {
66443             xtype: 'ColumnModel',
66444             xns: Roo.grid,
66445             dataIndex : 'weekday3',
66446             header : 'Wednesday',
66447             renderer : cellRender
66448         },
66449         {
66450             xtype: 'ColumnModel',
66451             xns: Roo.grid,
66452             dataIndex : 'weekday4',
66453             header : 'Thursday',
66454             renderer : cellRender
66455         },
66456         {
66457             xtype: 'ColumnModel',
66458             xns: Roo.grid,
66459             dataIndex : 'weekday5',
66460             header : 'Friday',
66461             renderer : cellRender
66462         },
66463         {
66464             xtype: 'ColumnModel',
66465             xns: Roo.grid,
66466             dataIndex : 'weekday6',
66467             header : 'Saturday',
66468             renderer : cellRender
66469         }
66470     ]);
66471     this.cm = this.colModel;
66472     this.cm.xmodule = this.xmodule || false;
66473  
66474         
66475           
66476     //this.selModel = new Roo.grid.CellSelectionModel();
66477     //this.sm = this.selModel;
66478     //this.selModel.init(this);
66479     
66480     
66481     if(this.width){
66482         this.container.setWidth(this.width);
66483     }
66484
66485     if(this.height){
66486         this.container.setHeight(this.height);
66487     }
66488     /** @private */
66489         this.addEvents({
66490         // raw events
66491         /**
66492          * @event click
66493          * The raw click event for the entire grid.
66494          * @param {Roo.EventObject} e
66495          */
66496         "click" : true,
66497         /**
66498          * @event dblclick
66499          * The raw dblclick event for the entire grid.
66500          * @param {Roo.EventObject} e
66501          */
66502         "dblclick" : true,
66503         /**
66504          * @event contextmenu
66505          * The raw contextmenu event for the entire grid.
66506          * @param {Roo.EventObject} e
66507          */
66508         "contextmenu" : true,
66509         /**
66510          * @event mousedown
66511          * The raw mousedown event for the entire grid.
66512          * @param {Roo.EventObject} e
66513          */
66514         "mousedown" : true,
66515         /**
66516          * @event mouseup
66517          * The raw mouseup event for the entire grid.
66518          * @param {Roo.EventObject} e
66519          */
66520         "mouseup" : true,
66521         /**
66522          * @event mouseover
66523          * The raw mouseover event for the entire grid.
66524          * @param {Roo.EventObject} e
66525          */
66526         "mouseover" : true,
66527         /**
66528          * @event mouseout
66529          * The raw mouseout event for the entire grid.
66530          * @param {Roo.EventObject} e
66531          */
66532         "mouseout" : true,
66533         /**
66534          * @event keypress
66535          * The raw keypress event for the entire grid.
66536          * @param {Roo.EventObject} e
66537          */
66538         "keypress" : true,
66539         /**
66540          * @event keydown
66541          * The raw keydown event for the entire grid.
66542          * @param {Roo.EventObject} e
66543          */
66544         "keydown" : true,
66545
66546         // custom events
66547
66548         /**
66549          * @event cellclick
66550          * Fires when a cell is clicked
66551          * @param {Grid} this
66552          * @param {Number} rowIndex
66553          * @param {Number} columnIndex
66554          * @param {Roo.EventObject} e
66555          */
66556         "cellclick" : true,
66557         /**
66558          * @event celldblclick
66559          * Fires when a cell is double clicked
66560          * @param {Grid} this
66561          * @param {Number} rowIndex
66562          * @param {Number} columnIndex
66563          * @param {Roo.EventObject} e
66564          */
66565         "celldblclick" : true,
66566         /**
66567          * @event rowclick
66568          * Fires when a row is clicked
66569          * @param {Grid} this
66570          * @param {Number} rowIndex
66571          * @param {Roo.EventObject} e
66572          */
66573         "rowclick" : true,
66574         /**
66575          * @event rowdblclick
66576          * Fires when a row is double clicked
66577          * @param {Grid} this
66578          * @param {Number} rowIndex
66579          * @param {Roo.EventObject} e
66580          */
66581         "rowdblclick" : true,
66582         /**
66583          * @event headerclick
66584          * Fires when a header is clicked
66585          * @param {Grid} this
66586          * @param {Number} columnIndex
66587          * @param {Roo.EventObject} e
66588          */
66589         "headerclick" : true,
66590         /**
66591          * @event headerdblclick
66592          * Fires when a header cell is double clicked
66593          * @param {Grid} this
66594          * @param {Number} columnIndex
66595          * @param {Roo.EventObject} e
66596          */
66597         "headerdblclick" : true,
66598         /**
66599          * @event rowcontextmenu
66600          * Fires when a row is right clicked
66601          * @param {Grid} this
66602          * @param {Number} rowIndex
66603          * @param {Roo.EventObject} e
66604          */
66605         "rowcontextmenu" : true,
66606         /**
66607          * @event cellcontextmenu
66608          * Fires when a cell is right clicked
66609          * @param {Grid} this
66610          * @param {Number} rowIndex
66611          * @param {Number} cellIndex
66612          * @param {Roo.EventObject} e
66613          */
66614          "cellcontextmenu" : true,
66615         /**
66616          * @event headercontextmenu
66617          * Fires when a header is right clicked
66618          * @param {Grid} this
66619          * @param {Number} columnIndex
66620          * @param {Roo.EventObject} e
66621          */
66622         "headercontextmenu" : true,
66623         /**
66624          * @event bodyscroll
66625          * Fires when the body element is scrolled
66626          * @param {Number} scrollLeft
66627          * @param {Number} scrollTop
66628          */
66629         "bodyscroll" : true,
66630         /**
66631          * @event columnresize
66632          * Fires when the user resizes a column
66633          * @param {Number} columnIndex
66634          * @param {Number} newSize
66635          */
66636         "columnresize" : true,
66637         /**
66638          * @event columnmove
66639          * Fires when the user moves a column
66640          * @param {Number} oldIndex
66641          * @param {Number} newIndex
66642          */
66643         "columnmove" : true,
66644         /**
66645          * @event startdrag
66646          * Fires when row(s) start being dragged
66647          * @param {Grid} this
66648          * @param {Roo.GridDD} dd The drag drop object
66649          * @param {event} e The raw browser event
66650          */
66651         "startdrag" : true,
66652         /**
66653          * @event enddrag
66654          * Fires when a drag operation is complete
66655          * @param {Grid} this
66656          * @param {Roo.GridDD} dd The drag drop object
66657          * @param {event} e The raw browser event
66658          */
66659         "enddrag" : true,
66660         /**
66661          * @event dragdrop
66662          * Fires when dragged row(s) are dropped on a valid DD target
66663          * @param {Grid} this
66664          * @param {Roo.GridDD} dd The drag drop object
66665          * @param {String} targetId The target drag drop object
66666          * @param {event} e The raw browser event
66667          */
66668         "dragdrop" : true,
66669         /**
66670          * @event dragover
66671          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66672          * @param {Grid} this
66673          * @param {Roo.GridDD} dd The drag drop object
66674          * @param {String} targetId The target drag drop object
66675          * @param {event} e The raw browser event
66676          */
66677         "dragover" : true,
66678         /**
66679          * @event dragenter
66680          *  Fires when the dragged row(s) first cross another DD target while being dragged
66681          * @param {Grid} this
66682          * @param {Roo.GridDD} dd The drag drop object
66683          * @param {String} targetId The target drag drop object
66684          * @param {event} e The raw browser event
66685          */
66686         "dragenter" : true,
66687         /**
66688          * @event dragout
66689          * Fires when the dragged row(s) leave another DD target while being dragged
66690          * @param {Grid} this
66691          * @param {Roo.GridDD} dd The drag drop object
66692          * @param {String} targetId The target drag drop object
66693          * @param {event} e The raw browser event
66694          */
66695         "dragout" : true,
66696         /**
66697          * @event rowclass
66698          * Fires when a row is rendered, so you can change add a style to it.
66699          * @param {GridView} gridview   The grid view
66700          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
66701          */
66702         'rowclass' : true,
66703
66704         /**
66705          * @event render
66706          * Fires when the grid is rendered
66707          * @param {Grid} grid
66708          */
66709         'render' : true,
66710             /**
66711              * @event select
66712              * Fires when a date is selected
66713              * @param {DatePicker} this
66714              * @param {Date} date The selected date
66715              */
66716         'select': true,
66717         /**
66718              * @event monthchange
66719              * Fires when the displayed month changes 
66720              * @param {DatePicker} this
66721              * @param {Date} date The selected month
66722              */
66723         'monthchange': true,
66724         /**
66725              * @event evententer
66726              * Fires when mouse over an event
66727              * @param {Calendar} this
66728              * @param {event} Event
66729              */
66730         'evententer': true,
66731         /**
66732              * @event eventleave
66733              * Fires when the mouse leaves an
66734              * @param {Calendar} this
66735              * @param {event}
66736              */
66737         'eventleave': true,
66738         /**
66739              * @event eventclick
66740              * Fires when the mouse click an
66741              * @param {Calendar} this
66742              * @param {event}
66743              */
66744         'eventclick': true,
66745         /**
66746              * @event eventrender
66747              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
66748              * @param {Calendar} this
66749              * @param {data} data to be modified
66750              */
66751         'eventrender': true
66752         
66753     });
66754
66755     Roo.grid.Grid.superclass.constructor.call(this);
66756     this.on('render', function() {
66757         this.view.el.addClass('x-grid-cal'); 
66758         
66759         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
66760
66761     },this);
66762     
66763     if (!Roo.grid.Calendar.style) {
66764         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
66765             
66766             
66767             '.x-grid-cal .x-grid-col' :  {
66768                 height: 'auto !important',
66769                 'vertical-align': 'top'
66770             },
66771             '.x-grid-cal  .fc-event-hori' : {
66772                 height: '14px'
66773             }
66774              
66775             
66776         }, Roo.id());
66777     }
66778
66779     
66780     
66781 };
66782 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
66783     /**
66784      * @cfg {Store} eventStore The store that loads events.
66785      */
66786     eventStore : 25,
66787
66788      
66789     activeDate : false,
66790     startDay : 0,
66791     autoWidth : true,
66792     monitorWindowResize : false,
66793
66794     
66795     resizeColumns : function() {
66796         var col = (this.view.el.getWidth() / 7) - 3;
66797         // loop through cols, and setWidth
66798         for(var i =0 ; i < 7 ; i++){
66799             this.cm.setColumnWidth(i, col);
66800         }
66801     },
66802      setDate :function(date) {
66803         
66804         Roo.log('setDate?');
66805         
66806         this.resizeColumns();
66807         var vd = this.activeDate;
66808         this.activeDate = date;
66809 //        if(vd && this.el){
66810 //            var t = date.getTime();
66811 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
66812 //                Roo.log('using add remove');
66813 //                
66814 //                this.fireEvent('monthchange', this, date);
66815 //                
66816 //                this.cells.removeClass("fc-state-highlight");
66817 //                this.cells.each(function(c){
66818 //                   if(c.dateValue == t){
66819 //                       c.addClass("fc-state-highlight");
66820 //                       setTimeout(function(){
66821 //                            try{c.dom.firstChild.focus();}catch(e){}
66822 //                       }, 50);
66823 //                       return false;
66824 //                   }
66825 //                   return true;
66826 //                });
66827 //                return;
66828 //            }
66829 //        }
66830         
66831         var days = date.getDaysInMonth();
66832         
66833         var firstOfMonth = date.getFirstDateOfMonth();
66834         var startingPos = firstOfMonth.getDay()-this.startDay;
66835         
66836         if(startingPos < this.startDay){
66837             startingPos += 7;
66838         }
66839         
66840         var pm = date.add(Date.MONTH, -1);
66841         var prevStart = pm.getDaysInMonth()-startingPos;
66842 //        
66843         
66844         
66845         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66846         
66847         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
66848         //this.cells.addClassOnOver('fc-state-hover');
66849         
66850         var cells = this.cells.elements;
66851         var textEls = this.textNodes;
66852         
66853         //Roo.each(cells, function(cell){
66854         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
66855         //});
66856         
66857         days += startingPos;
66858
66859         // convert everything to numbers so it's fast
66860         var day = 86400000;
66861         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
66862         //Roo.log(d);
66863         //Roo.log(pm);
66864         //Roo.log(prevStart);
66865         
66866         var today = new Date().clearTime().getTime();
66867         var sel = date.clearTime().getTime();
66868         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
66869         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
66870         var ddMatch = this.disabledDatesRE;
66871         var ddText = this.disabledDatesText;
66872         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
66873         var ddaysText = this.disabledDaysText;
66874         var format = this.format;
66875         
66876         var setCellClass = function(cal, cell){
66877             
66878             //Roo.log('set Cell Class');
66879             cell.title = "";
66880             var t = d.getTime();
66881             
66882             //Roo.log(d);
66883             
66884             
66885             cell.dateValue = t;
66886             if(t == today){
66887                 cell.className += " fc-today";
66888                 cell.className += " fc-state-highlight";
66889                 cell.title = cal.todayText;
66890             }
66891             if(t == sel){
66892                 // disable highlight in other month..
66893                 cell.className += " fc-state-highlight";
66894                 
66895             }
66896             // disabling
66897             if(t < min) {
66898                 //cell.className = " fc-state-disabled";
66899                 cell.title = cal.minText;
66900                 return;
66901             }
66902             if(t > max) {
66903                 //cell.className = " fc-state-disabled";
66904                 cell.title = cal.maxText;
66905                 return;
66906             }
66907             if(ddays){
66908                 if(ddays.indexOf(d.getDay()) != -1){
66909                     // cell.title = ddaysText;
66910                    // cell.className = " fc-state-disabled";
66911                 }
66912             }
66913             if(ddMatch && format){
66914                 var fvalue = d.dateFormat(format);
66915                 if(ddMatch.test(fvalue)){
66916                     cell.title = ddText.replace("%0", fvalue);
66917                    cell.className = " fc-state-disabled";
66918                 }
66919             }
66920             
66921             if (!cell.initialClassName) {
66922                 cell.initialClassName = cell.dom.className;
66923             }
66924             
66925             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
66926         };
66927
66928         var i = 0;
66929         
66930         for(; i < startingPos; i++) {
66931             cells[i].dayName =  (++prevStart);
66932             Roo.log(textEls[i]);
66933             d.setDate(d.getDate()+1);
66934             
66935             //cells[i].className = "fc-past fc-other-month";
66936             setCellClass(this, cells[i]);
66937         }
66938         
66939         var intDay = 0;
66940         
66941         for(; i < days; i++){
66942             intDay = i - startingPos + 1;
66943             cells[i].dayName =  (intDay);
66944             d.setDate(d.getDate()+1);
66945             
66946             cells[i].className = ''; // "x-date-active";
66947             setCellClass(this, cells[i]);
66948         }
66949         var extraDays = 0;
66950         
66951         for(; i < 42; i++) {
66952             //textEls[i].innerHTML = (++extraDays);
66953             
66954             d.setDate(d.getDate()+1);
66955             cells[i].dayName = (++extraDays);
66956             cells[i].className = "fc-future fc-other-month";
66957             setCellClass(this, cells[i]);
66958         }
66959         
66960         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
66961         
66962         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
66963         
66964         // this will cause all the cells to mis
66965         var rows= [];
66966         var i =0;
66967         for (var r = 0;r < 6;r++) {
66968             for (var c =0;c < 7;c++) {
66969                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
66970             }    
66971         }
66972         
66973         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66974         for(i=0;i<cells.length;i++) {
66975             
66976             this.cells.elements[i].dayName = cells[i].dayName ;
66977             this.cells.elements[i].className = cells[i].className;
66978             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
66979             this.cells.elements[i].title = cells[i].title ;
66980             this.cells.elements[i].dateValue = cells[i].dateValue ;
66981         }
66982         
66983         
66984         
66985         
66986         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
66987         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
66988         
66989         ////if(totalRows != 6){
66990             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
66991            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
66992        // }
66993         
66994         this.fireEvent('monthchange', this, date);
66995         
66996         
66997     },
66998  /**
66999      * Returns the grid's SelectionModel.
67000      * @return {SelectionModel}
67001      */
67002     getSelectionModel : function(){
67003         if(!this.selModel){
67004             this.selModel = new Roo.grid.CellSelectionModel();
67005         }
67006         return this.selModel;
67007     },
67008
67009     load: function() {
67010         this.eventStore.load()
67011         
67012         
67013         
67014     },
67015     
67016     findCell : function(dt) {
67017         dt = dt.clearTime().getTime();
67018         var ret = false;
67019         this.cells.each(function(c){
67020             //Roo.log("check " +c.dateValue + '?=' + dt);
67021             if(c.dateValue == dt){
67022                 ret = c;
67023                 return false;
67024             }
67025             return true;
67026         });
67027         
67028         return ret;
67029     },
67030     
67031     findCells : function(rec) {
67032         var s = rec.data.start_dt.clone().clearTime().getTime();
67033        // Roo.log(s);
67034         var e= rec.data.end_dt.clone().clearTime().getTime();
67035        // Roo.log(e);
67036         var ret = [];
67037         this.cells.each(function(c){
67038              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67039             
67040             if(c.dateValue > e){
67041                 return ;
67042             }
67043             if(c.dateValue < s){
67044                 return ;
67045             }
67046             ret.push(c);
67047         });
67048         
67049         return ret;    
67050     },
67051     
67052     findBestRow: function(cells)
67053     {
67054         var ret = 0;
67055         
67056         for (var i =0 ; i < cells.length;i++) {
67057             ret  = Math.max(cells[i].rows || 0,ret);
67058         }
67059         return ret;
67060         
67061     },
67062     
67063     
67064     addItem : function(rec)
67065     {
67066         // look for vertical location slot in
67067         var cells = this.findCells(rec);
67068         
67069         rec.row = this.findBestRow(cells);
67070         
67071         // work out the location.
67072         
67073         var crow = false;
67074         var rows = [];
67075         for(var i =0; i < cells.length; i++) {
67076             if (!crow) {
67077                 crow = {
67078                     start : cells[i],
67079                     end :  cells[i]
67080                 };
67081                 continue;
67082             }
67083             if (crow.start.getY() == cells[i].getY()) {
67084                 // on same row.
67085                 crow.end = cells[i];
67086                 continue;
67087             }
67088             // different row.
67089             rows.push(crow);
67090             crow = {
67091                 start: cells[i],
67092                 end : cells[i]
67093             };
67094             
67095         }
67096         
67097         rows.push(crow);
67098         rec.els = [];
67099         rec.rows = rows;
67100         rec.cells = cells;
67101         for (var i = 0; i < cells.length;i++) {
67102             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67103             
67104         }
67105         
67106         
67107     },
67108     
67109     clearEvents: function() {
67110         
67111         if (!this.eventStore.getCount()) {
67112             return;
67113         }
67114         // reset number of rows in cells.
67115         Roo.each(this.cells.elements, function(c){
67116             c.rows = 0;
67117         });
67118         
67119         this.eventStore.each(function(e) {
67120             this.clearEvent(e);
67121         },this);
67122         
67123     },
67124     
67125     clearEvent : function(ev)
67126     {
67127         if (ev.els) {
67128             Roo.each(ev.els, function(el) {
67129                 el.un('mouseenter' ,this.onEventEnter, this);
67130                 el.un('mouseleave' ,this.onEventLeave, this);
67131                 el.remove();
67132             },this);
67133             ev.els = [];
67134         }
67135     },
67136     
67137     
67138     renderEvent : function(ev,ctr) {
67139         if (!ctr) {
67140              ctr = this.view.el.select('.fc-event-container',true).first();
67141         }
67142         
67143          
67144         this.clearEvent(ev);
67145             //code
67146        
67147         
67148         
67149         ev.els = [];
67150         var cells = ev.cells;
67151         var rows = ev.rows;
67152         this.fireEvent('eventrender', this, ev);
67153         
67154         for(var i =0; i < rows.length; i++) {
67155             
67156             cls = '';
67157             if (i == 0) {
67158                 cls += ' fc-event-start';
67159             }
67160             if ((i+1) == rows.length) {
67161                 cls += ' fc-event-end';
67162             }
67163             
67164             //Roo.log(ev.data);
67165             // how many rows should it span..
67166             var cg = this.eventTmpl.append(ctr,Roo.apply({
67167                 fccls : cls
67168                 
67169             }, ev.data) , true);
67170             
67171             
67172             cg.on('mouseenter' ,this.onEventEnter, this, ev);
67173             cg.on('mouseleave' ,this.onEventLeave, this, ev);
67174             cg.on('click', this.onEventClick, this, ev);
67175             
67176             ev.els.push(cg);
67177             
67178             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67179             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67180             //Roo.log(cg);
67181              
67182             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
67183             cg.setWidth(ebox.right - sbox.x -2);
67184         }
67185     },
67186     
67187     renderEvents: function()
67188     {   
67189         // first make sure there is enough space..
67190         
67191         if (!this.eventTmpl) {
67192             this.eventTmpl = new Roo.Template(
67193                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
67194                     '<div class="fc-event-inner">' +
67195                         '<span class="fc-event-time">{time}</span>' +
67196                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67197                     '</div>' +
67198                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
67199                 '</div>'
67200             );
67201                 
67202         }
67203                
67204         
67205         
67206         this.cells.each(function(c) {
67207             //Roo.log(c.select('.fc-day-content div',true).first());
67208             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67209         });
67210         
67211         var ctr = this.view.el.select('.fc-event-container',true).first();
67212         
67213         var cls;
67214         this.eventStore.each(function(ev){
67215             
67216             this.renderEvent(ev);
67217              
67218              
67219         }, this);
67220         this.view.layout();
67221         
67222     },
67223     
67224     onEventEnter: function (e, el,event,d) {
67225         this.fireEvent('evententer', this, el, event);
67226     },
67227     
67228     onEventLeave: function (e, el,event,d) {
67229         this.fireEvent('eventleave', this, el, event);
67230     },
67231     
67232     onEventClick: function (e, el,event,d) {
67233         this.fireEvent('eventclick', this, el, event);
67234     },
67235     
67236     onMonthChange: function () {
67237         this.store.load();
67238     },
67239     
67240     onLoad: function () {
67241         
67242         //Roo.log('calendar onload');
67243 //         
67244         if(this.eventStore.getCount() > 0){
67245             
67246            
67247             
67248             this.eventStore.each(function(d){
67249                 
67250                 
67251                 // FIXME..
67252                 var add =   d.data;
67253                 if (typeof(add.end_dt) == 'undefined')  {
67254                     Roo.log("Missing End time in calendar data: ");
67255                     Roo.log(d);
67256                     return;
67257                 }
67258                 if (typeof(add.start_dt) == 'undefined')  {
67259                     Roo.log("Missing Start time in calendar data: ");
67260                     Roo.log(d);
67261                     return;
67262                 }
67263                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67264                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67265                 add.id = add.id || d.id;
67266                 add.title = add.title || '??';
67267                 
67268                 this.addItem(d);
67269                 
67270              
67271             },this);
67272         }
67273         
67274         this.renderEvents();
67275     }
67276     
67277
67278 });
67279 /*
67280  grid : {
67281                 xtype: 'Grid',
67282                 xns: Roo.grid,
67283                 listeners : {
67284                     render : function ()
67285                     {
67286                         _this.grid = this;
67287                         
67288                         if (!this.view.el.hasClass('course-timesheet')) {
67289                             this.view.el.addClass('course-timesheet');
67290                         }
67291                         if (this.tsStyle) {
67292                             this.ds.load({});
67293                             return; 
67294                         }
67295                         Roo.log('width');
67296                         Roo.log(_this.grid.view.el.getWidth());
67297                         
67298                         
67299                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
67300                             '.course-timesheet .x-grid-row' : {
67301                                 height: '80px'
67302                             },
67303                             '.x-grid-row td' : {
67304                                 'vertical-align' : 0
67305                             },
67306                             '.course-edit-link' : {
67307                                 'color' : 'blue',
67308                                 'text-overflow' : 'ellipsis',
67309                                 'overflow' : 'hidden',
67310                                 'white-space' : 'nowrap',
67311                                 'cursor' : 'pointer'
67312                             },
67313                             '.sub-link' : {
67314                                 'color' : 'green'
67315                             },
67316                             '.de-act-sup-link' : {
67317                                 'color' : 'purple',
67318                                 'text-decoration' : 'line-through'
67319                             },
67320                             '.de-act-link' : {
67321                                 'color' : 'red',
67322                                 'text-decoration' : 'line-through'
67323                             },
67324                             '.course-timesheet .course-highlight' : {
67325                                 'border-top-style': 'dashed !important',
67326                                 'border-bottom-bottom': 'dashed !important'
67327                             },
67328                             '.course-timesheet .course-item' : {
67329                                 'font-family'   : 'tahoma, arial, helvetica',
67330                                 'font-size'     : '11px',
67331                                 'overflow'      : 'hidden',
67332                                 'padding-left'  : '10px',
67333                                 'padding-right' : '10px',
67334                                 'padding-top' : '10px' 
67335                             }
67336                             
67337                         }, Roo.id());
67338                                 this.ds.load({});
67339                     }
67340                 },
67341                 autoWidth : true,
67342                 monitorWindowResize : false,
67343                 cellrenderer : function(v,x,r)
67344                 {
67345                     return v;
67346                 },
67347                 sm : {
67348                     xtype: 'CellSelectionModel',
67349                     xns: Roo.grid
67350                 },
67351                 dataSource : {
67352                     xtype: 'Store',
67353                     xns: Roo.data,
67354                     listeners : {
67355                         beforeload : function (_self, options)
67356                         {
67357                             options.params = options.params || {};
67358                             options.params._month = _this.monthField.getValue();
67359                             options.params.limit = 9999;
67360                             options.params['sort'] = 'when_dt';    
67361                             options.params['dir'] = 'ASC';    
67362                             this.proxy.loadResponse = this.loadResponse;
67363                             Roo.log("load?");
67364                             //this.addColumns();
67365                         },
67366                         load : function (_self, records, options)
67367                         {
67368                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67369                                 // if you click on the translation.. you can edit it...
67370                                 var el = Roo.get(this);
67371                                 var id = el.dom.getAttribute('data-id');
67372                                 var d = el.dom.getAttribute('data-date');
67373                                 var t = el.dom.getAttribute('data-time');
67374                                 //var id = this.child('span').dom.textContent;
67375                                 
67376                                 //Roo.log(this);
67377                                 Pman.Dialog.CourseCalendar.show({
67378                                     id : id,
67379                                     when_d : d,
67380                                     when_t : t,
67381                                     productitem_active : id ? 1 : 0
67382                                 }, function() {
67383                                     _this.grid.ds.load({});
67384                                 });
67385                            
67386                            });
67387                            
67388                            _this.panel.fireEvent('resize', [ '', '' ]);
67389                         }
67390                     },
67391                     loadResponse : function(o, success, response){
67392                             // this is overridden on before load..
67393                             
67394                             Roo.log("our code?");       
67395                             //Roo.log(success);
67396                             //Roo.log(response)
67397                             delete this.activeRequest;
67398                             if(!success){
67399                                 this.fireEvent("loadexception", this, o, response);
67400                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67401                                 return;
67402                             }
67403                             var result;
67404                             try {
67405                                 result = o.reader.read(response);
67406                             }catch(e){
67407                                 Roo.log("load exception?");
67408                                 this.fireEvent("loadexception", this, o, response, e);
67409                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67410                                 return;
67411                             }
67412                             Roo.log("ready...");        
67413                             // loop through result.records;
67414                             // and set this.tdate[date] = [] << array of records..
67415                             _this.tdata  = {};
67416                             Roo.each(result.records, function(r){
67417                                 //Roo.log(r.data);
67418                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67419                                     _this.tdata[r.data.when_dt.format('j')] = [];
67420                                 }
67421                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67422                             });
67423                             
67424                             //Roo.log(_this.tdata);
67425                             
67426                             result.records = [];
67427                             result.totalRecords = 6;
67428                     
67429                             // let's generate some duumy records for the rows.
67430                             //var st = _this.dateField.getValue();
67431                             
67432                             // work out monday..
67433                             //st = st.add(Date.DAY, -1 * st.format('w'));
67434                             
67435                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67436                             
67437                             var firstOfMonth = date.getFirstDayOfMonth();
67438                             var days = date.getDaysInMonth();
67439                             var d = 1;
67440                             var firstAdded = false;
67441                             for (var i = 0; i < result.totalRecords ; i++) {
67442                                 //var d= st.add(Date.DAY, i);
67443                                 var row = {};
67444                                 var added = 0;
67445                                 for(var w = 0 ; w < 7 ; w++){
67446                                     if(!firstAdded && firstOfMonth != w){
67447                                         continue;
67448                                     }
67449                                     if(d > days){
67450                                         continue;
67451                                     }
67452                                     firstAdded = true;
67453                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
67454                                     row['weekday'+w] = String.format(
67455                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
67456                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67457                                                     d,
67458                                                     date.format('Y-m-')+dd
67459                                                 );
67460                                     added++;
67461                                     if(typeof(_this.tdata[d]) != 'undefined'){
67462                                         Roo.each(_this.tdata[d], function(r){
67463                                             var is_sub = '';
67464                                             var deactive = '';
67465                                             var id = r.id;
67466                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67467                                             if(r.parent_id*1>0){
67468                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67469                                                 id = r.parent_id;
67470                                             }
67471                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67472                                                 deactive = 'de-act-link';
67473                                             }
67474                                             
67475                                             row['weekday'+w] += String.format(
67476                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67477                                                     id, //0
67478                                                     r.product_id_name, //1
67479                                                     r.when_dt.format('h:ia'), //2
67480                                                     is_sub, //3
67481                                                     deactive, //4
67482                                                     desc // 5
67483                                             );
67484                                         });
67485                                     }
67486                                     d++;
67487                                 }
67488                                 
67489                                 // only do this if something added..
67490                                 if(added > 0){ 
67491                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
67492                                 }
67493                                 
67494                                 
67495                                 // push it twice. (second one with an hour..
67496                                 
67497                             }
67498                             //Roo.log(result);
67499                             this.fireEvent("load", this, o, o.request.arg);
67500                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
67501                         },
67502                     sortInfo : {field: 'when_dt', direction : 'ASC' },
67503                     proxy : {
67504                         xtype: 'HttpProxy',
67505                         xns: Roo.data,
67506                         method : 'GET',
67507                         url : baseURL + '/Roo/Shop_course.php'
67508                     },
67509                     reader : {
67510                         xtype: 'JsonReader',
67511                         xns: Roo.data,
67512                         id : 'id',
67513                         fields : [
67514                             {
67515                                 'name': 'id',
67516                                 'type': 'int'
67517                             },
67518                             {
67519                                 'name': 'when_dt',
67520                                 'type': 'string'
67521                             },
67522                             {
67523                                 'name': 'end_dt',
67524                                 'type': 'string'
67525                             },
67526                             {
67527                                 'name': 'parent_id',
67528                                 'type': 'int'
67529                             },
67530                             {
67531                                 'name': 'product_id',
67532                                 'type': 'int'
67533                             },
67534                             {
67535                                 'name': 'productitem_id',
67536                                 'type': 'int'
67537                             },
67538                             {
67539                                 'name': 'guid',
67540                                 'type': 'int'
67541                             }
67542                         ]
67543                     }
67544                 },
67545                 toolbar : {
67546                     xtype: 'Toolbar',
67547                     xns: Roo,
67548                     items : [
67549                         {
67550                             xtype: 'Button',
67551                             xns: Roo.Toolbar,
67552                             listeners : {
67553                                 click : function (_self, e)
67554                                 {
67555                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67556                                     sd.setMonth(sd.getMonth()-1);
67557                                     _this.monthField.setValue(sd.format('Y-m-d'));
67558                                     _this.grid.ds.load({});
67559                                 }
67560                             },
67561                             text : "Back"
67562                         },
67563                         {
67564                             xtype: 'Separator',
67565                             xns: Roo.Toolbar
67566                         },
67567                         {
67568                             xtype: 'MonthField',
67569                             xns: Roo.form,
67570                             listeners : {
67571                                 render : function (_self)
67572                                 {
67573                                     _this.monthField = _self;
67574                                    // _this.monthField.set  today
67575                                 },
67576                                 select : function (combo, date)
67577                                 {
67578                                     _this.grid.ds.load({});
67579                                 }
67580                             },
67581                             value : (function() { return new Date(); })()
67582                         },
67583                         {
67584                             xtype: 'Separator',
67585                             xns: Roo.Toolbar
67586                         },
67587                         {
67588                             xtype: 'TextItem',
67589                             xns: Roo.Toolbar,
67590                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67591                         },
67592                         {
67593                             xtype: 'Fill',
67594                             xns: Roo.Toolbar
67595                         },
67596                         {
67597                             xtype: 'Button',
67598                             xns: Roo.Toolbar,
67599                             listeners : {
67600                                 click : function (_self, e)
67601                                 {
67602                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67603                                     sd.setMonth(sd.getMonth()+1);
67604                                     _this.monthField.setValue(sd.format('Y-m-d'));
67605                                     _this.grid.ds.load({});
67606                                 }
67607                             },
67608                             text : "Next"
67609                         }
67610                     ]
67611                 },
67612                  
67613             }
67614         };
67615         
67616         *//*
67617  * Based on:
67618  * Ext JS Library 1.1.1
67619  * Copyright(c) 2006-2007, Ext JS, LLC.
67620  *
67621  * Originally Released Under LGPL - original licence link has changed is not relivant.
67622  *
67623  * Fork - LGPL
67624  * <script type="text/javascript">
67625  */
67626  
67627 /**
67628  * @class Roo.LoadMask
67629  * A simple utility class for generically masking elements while loading data.  If the element being masked has
67630  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67631  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
67632  * element's UpdateManager load indicator and will be destroyed after the initial load.
67633  * @constructor
67634  * Create a new LoadMask
67635  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67636  * @param {Object} config The config object
67637  */
67638 Roo.LoadMask = function(el, config){
67639     this.el = Roo.get(el);
67640     Roo.apply(this, config);
67641     if(this.store){
67642         this.store.on('beforeload', this.onBeforeLoad, this);
67643         this.store.on('load', this.onLoad, this);
67644         this.store.on('loadexception', this.onLoadException, this);
67645         this.removeMask = false;
67646     }else{
67647         var um = this.el.getUpdateManager();
67648         um.showLoadIndicator = false; // disable the default indicator
67649         um.on('beforeupdate', this.onBeforeLoad, this);
67650         um.on('update', this.onLoad, this);
67651         um.on('failure', this.onLoad, this);
67652         this.removeMask = true;
67653     }
67654 };
67655
67656 Roo.LoadMask.prototype = {
67657     /**
67658      * @cfg {Boolean} removeMask
67659      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67660      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
67661      */
67662     removeMask : false,
67663     /**
67664      * @cfg {String} msg
67665      * The text to display in a centered loading message box (defaults to 'Loading...')
67666      */
67667     msg : 'Loading...',
67668     /**
67669      * @cfg {String} msgCls
67670      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67671      */
67672     msgCls : 'x-mask-loading',
67673
67674     /**
67675      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67676      * @type Boolean
67677      */
67678     disabled: false,
67679
67680     /**
67681      * Disables the mask to prevent it from being displayed
67682      */
67683     disable : function(){
67684        this.disabled = true;
67685     },
67686
67687     /**
67688      * Enables the mask so that it can be displayed
67689      */
67690     enable : function(){
67691         this.disabled = false;
67692     },
67693     
67694     onLoadException : function()
67695     {
67696         Roo.log(arguments);
67697         
67698         if (typeof(arguments[3]) != 'undefined') {
67699             Roo.MessageBox.alert("Error loading",arguments[3]);
67700         } 
67701         /*
67702         try {
67703             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67704                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67705             }   
67706         } catch(e) {
67707             
67708         }
67709         */
67710     
67711         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67712     },
67713     // private
67714     onLoad : function()
67715     {
67716         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67717     },
67718
67719     // private
67720     onBeforeLoad : function(){
67721         if(!this.disabled){
67722             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67723         }
67724     },
67725
67726     // private
67727     destroy : function(){
67728         if(this.store){
67729             this.store.un('beforeload', this.onBeforeLoad, this);
67730             this.store.un('load', this.onLoad, this);
67731             this.store.un('loadexception', this.onLoadException, this);
67732         }else{
67733             var um = this.el.getUpdateManager();
67734             um.un('beforeupdate', this.onBeforeLoad, this);
67735             um.un('update', this.onLoad, this);
67736             um.un('failure', this.onLoad, this);
67737         }
67738     }
67739 };/*
67740  * Based on:
67741  * Ext JS Library 1.1.1
67742  * Copyright(c) 2006-2007, Ext JS, LLC.
67743  *
67744  * Originally Released Under LGPL - original licence link has changed is not relivant.
67745  *
67746  * Fork - LGPL
67747  * <script type="text/javascript">
67748  */
67749
67750
67751 /**
67752  * @class Roo.XTemplate
67753  * @extends Roo.Template
67754  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
67755 <pre><code>
67756 var t = new Roo.XTemplate(
67757         '&lt;select name="{name}"&gt;',
67758                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
67759         '&lt;/select&gt;'
67760 );
67761  
67762 // then append, applying the master template values
67763  </code></pre>
67764  *
67765  * Supported features:
67766  *
67767  *  Tags:
67768
67769 <pre><code>
67770       {a_variable} - output encoded.
67771       {a_variable.format:("Y-m-d")} - call a method on the variable
67772       {a_variable:raw} - unencoded output
67773       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
67774       {a_variable:this.method_on_template(...)} - call a method on the template object.
67775  
67776 </code></pre>
67777  *  The tpl tag:
67778 <pre><code>
67779         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
67780         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
67781         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
67782         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
67783   
67784         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
67785         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
67786 </code></pre>
67787  *      
67788  */
67789 Roo.XTemplate = function()
67790 {
67791     Roo.XTemplate.superclass.constructor.apply(this, arguments);
67792     if (this.html) {
67793         this.compile();
67794     }
67795 };
67796
67797
67798 Roo.extend(Roo.XTemplate, Roo.Template, {
67799
67800     /**
67801      * The various sub templates
67802      */
67803     tpls : false,
67804     /**
67805      *
67806      * basic tag replacing syntax
67807      * WORD:WORD()
67808      *
67809      * // you can fake an object call by doing this
67810      *  x.t:(test,tesT) 
67811      * 
67812      */
67813     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
67814
67815     /**
67816      * compile the template
67817      *
67818      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
67819      *
67820      */
67821     compile: function()
67822     {
67823         var s = this.html;
67824      
67825         s = ['<tpl>', s, '</tpl>'].join('');
67826     
67827         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
67828             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
67829             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
67830             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
67831             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
67832             m,
67833             id     = 0,
67834             tpls   = [];
67835     
67836         while(true == !!(m = s.match(re))){
67837             var forMatch   = m[0].match(nameRe),
67838                 ifMatch   = m[0].match(ifRe),
67839                 execMatch   = m[0].match(execRe),
67840                 namedMatch   = m[0].match(namedRe),
67841                 
67842                 exp  = null, 
67843                 fn   = null,
67844                 exec = null,
67845                 name = forMatch && forMatch[1] ? forMatch[1] : '';
67846                 
67847             if (ifMatch) {
67848                 // if - puts fn into test..
67849                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
67850                 if(exp){
67851                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
67852                 }
67853             }
67854             
67855             if (execMatch) {
67856                 // exec - calls a function... returns empty if true is  returned.
67857                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
67858                 if(exp){
67859                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
67860                 }
67861             }
67862             
67863             
67864             if (name) {
67865                 // for = 
67866                 switch(name){
67867                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
67868                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
67869                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
67870                 }
67871             }
67872             var uid = namedMatch ? namedMatch[1] : id;
67873             
67874             
67875             tpls.push({
67876                 id:     namedMatch ? namedMatch[1] : id,
67877                 target: name,
67878                 exec:   exec,
67879                 test:   fn,
67880                 body:   m[1] || ''
67881             });
67882             if (namedMatch) {
67883                 s = s.replace(m[0], '');
67884             } else { 
67885                 s = s.replace(m[0], '{xtpl'+ id + '}');
67886             }
67887             ++id;
67888         }
67889         this.tpls = [];
67890         for(var i = tpls.length-1; i >= 0; --i){
67891             this.compileTpl(tpls[i]);
67892             this.tpls[tpls[i].id] = tpls[i];
67893         }
67894         this.master = tpls[tpls.length-1];
67895         return this;
67896     },
67897     /**
67898      * same as applyTemplate, except it's done to one of the subTemplates
67899      * when using named templates, you can do:
67900      *
67901      * var str = pl.applySubTemplate('your-name', values);
67902      *
67903      * 
67904      * @param {Number} id of the template
67905      * @param {Object} values to apply to template
67906      * @param {Object} parent (normaly the instance of this object)
67907      */
67908     applySubTemplate : function(id, values, parent)
67909     {
67910         
67911         
67912         var t = this.tpls[id];
67913         
67914         
67915         try { 
67916             if(t.test && !t.test.call(this, values, parent)){
67917                 return '';
67918             }
67919         } catch(e) {
67920             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
67921             Roo.log(e.toString());
67922             Roo.log(t.test);
67923             return ''
67924         }
67925         try { 
67926             
67927             if(t.exec && t.exec.call(this, values, parent)){
67928                 return '';
67929             }
67930         } catch(e) {
67931             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
67932             Roo.log(e.toString());
67933             Roo.log(t.exec);
67934             return ''
67935         }
67936         try {
67937             var vs = t.target ? t.target.call(this, values, parent) : values;
67938             parent = t.target ? values : parent;
67939             if(t.target && vs instanceof Array){
67940                 var buf = [];
67941                 for(var i = 0, len = vs.length; i < len; i++){
67942                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
67943                 }
67944                 return buf.join('');
67945             }
67946             return t.compiled.call(this, vs, parent);
67947         } catch (e) {
67948             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
67949             Roo.log(e.toString());
67950             Roo.log(t.compiled);
67951             return '';
67952         }
67953     },
67954
67955     compileTpl : function(tpl)
67956     {
67957         var fm = Roo.util.Format;
67958         var useF = this.disableFormats !== true;
67959         var sep = Roo.isGecko ? "+" : ",";
67960         var undef = function(str) {
67961             Roo.log("Property not found :"  + str);
67962             return '';
67963         };
67964         
67965         var fn = function(m, name, format, args)
67966         {
67967             //Roo.log(arguments);
67968             args = args ? args.replace(/\\'/g,"'") : args;
67969             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
67970             if (typeof(format) == 'undefined') {
67971                 format= 'htmlEncode';
67972             }
67973             if (format == 'raw' ) {
67974                 format = false;
67975             }
67976             
67977             if(name.substr(0, 4) == 'xtpl'){
67978                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
67979             }
67980             
67981             // build an array of options to determine if value is undefined..
67982             
67983             // basically get 'xxxx.yyyy' then do
67984             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
67985             //    (function () { Roo.log("Property not found"); return ''; })() :
67986             //    ......
67987             
67988             var udef_ar = [];
67989             var lookfor = '';
67990             Roo.each(name.split('.'), function(st) {
67991                 lookfor += (lookfor.length ? '.': '') + st;
67992                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
67993             });
67994             
67995             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
67996             
67997             
67998             if(format && useF){
67999                 
68000                 args = args ? ',' + args : "";
68001                  
68002                 if(format.substr(0, 5) != "this."){
68003                     format = "fm." + format + '(';
68004                 }else{
68005                     format = 'this.call("'+ format.substr(5) + '", ';
68006                     args = ", values";
68007                 }
68008                 
68009                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
68010             }
68011              
68012             if (args.length) {
68013                 // called with xxyx.yuu:(test,test)
68014                 // change to ()
68015                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
68016             }
68017             // raw.. - :raw modifier..
68018             return "'"+ sep + udef_st  + name + ")"+sep+"'";
68019             
68020         };
68021         var body;
68022         // branched to use + in gecko and [].join() in others
68023         if(Roo.isGecko){
68024             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
68025                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68026                     "';};};";
68027         }else{
68028             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
68029             body.push(tpl.body.replace(/(\r\n|\n)/g,
68030                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68031             body.push("'].join('');};};");
68032             body = body.join('');
68033         }
68034         
68035         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68036        
68037         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
68038         eval(body);
68039         
68040         return this;
68041     },
68042
68043     applyTemplate : function(values){
68044         return this.master.compiled.call(this, values, {});
68045         //var s = this.subs;
68046     },
68047
68048     apply : function(){
68049         return this.applyTemplate.apply(this, arguments);
68050     }
68051
68052  });
68053
68054 Roo.XTemplate.from = function(el){
68055     el = Roo.getDom(el);
68056     return new Roo.XTemplate(el.value || el.innerHTML);
68057 };